[
  {
    "path": ".codecov.yml",
    "content": "codecov:\n  notify:\n    require_ci_to_pass: yes\n\ncoverage:\n  precision: 2\n  round: down\n  range: \"1...100\"\n  status:\n    project: no\n    patch: no\n    changes: no\n\nignore:\n  - \"**/*.gen.go\"\n  - \"**/generated.go\"\n  - \"**/*.pb.go\"\n  - \"**/*.pb.gw.go\"\n  - \"**/gen/**\"\n\nflags:\n  go.unittests:\n    carryforward: true\n  js.unittests:\n    carryforward: true\n"
  },
  {
    "path": ".dockerignore",
    "content": "*#\n*~\n.#*\n.DS_Store\n.agignore\n.env\n.projectile\nDockerfile\ncore-sources.jar\ncoverage.txt\ndist/\ngen.sum.tmp\ngin-bin\ngoogle-services.json\njs/\nout/\nprofile.out\nvendor/\ntool/\n"
  },
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\ncharset = utf-8\n\nend_of_line = lf\ninsert_final_newline = true\ntrim_trailing_whitespace = true\n\nindent_style = space\nindent_size = 4\n\n[Makefile]\nindent_style = tab\n\n[*.go]\nindent_style = tab\n\n[*.proto]\nindent_size = 2\n\n[*.swift]\nindent_size = 4\n\n[*.tmpl]\nindent_size = 2\n\n[*.{js,jsx,ts,tsx}]\nindent_size = 2\nindent_style = tab\nblock_comment_start = /*\nblock_comment_end = */\n\n[*.hbs]\nindent_size = 4\nindent_style = tab\nblock_comment_start = {{!\nblock_comment_end = }}\n\n[*.html]\nindent_size = 2\n\n[*.bat]\nend_of_line = crlf\n\n[*.{json,yml}]\nindent_size = 2\nindent_style = space\n\n[.{babelrc,eslintrc}]\nindent_size = 2\n\n[{Fastfile,.buckconfig,BUCK}]\nindent_size = 2\n\n[*.diff]\nindent_size = 1\n\n[*.m]\nindent_size = 1\nindent_style = space\nblock_comment_start = /**\nblock_comment = *\nblock_comment_end = */\n\n[*.java]\nindent_size = 4\nindent_style = space\nblock_comment_start = /**\nblock_comment = *\nblock_comment_end = */\n\n[js/packages/i18n/locale/**/*.{json,yml}]\nindent_size = 2\nindent_style = tab\n"
  },
  {
    "path": ".gitattributes",
    "content": "# Auto detect text files and perform LF normalization\n* text=auto\n\n# Collapse vendored files on GitHub\nvendor/* linguist-vendored\n*/vendor/* linguist-vendored\nclient/common/openssl/built/* linguist-vendored\n\n# Collapse generated files on GitHub\ngen.sum linguist-generated merge=ours -diff\n*.gen.go linguist-generated merge=ours -diff\n*.gen.graphql linguist-generated merge=ours -diff\n*.gen.js linguist-generated merge=ours -diff\n*.gen.json linguist-generated merge=ours -diff\n*.gen.ts linguist-generated merge=ours -diff\n*.gen.tsx linguist-generated merge=ours -diff\n*.gen.yml linguist-generated merge=ours -diff\n*.pb.d.ts linguist-generated merge=ours -diff\n*.pb.go linguist-generated merge=ours -diff\n*.pb.gw.go linguist-generated merge=ours -diff\n*.pb.js linguist-generated merge=ours -diff\n*pb_test.go linguist-generated merge=ours -diff\ndocs/apis/*types.md linguist-generated merge=ours -diff\n*.swagger.json linguist-generated merge=ours -diff\napi/*.yaml linguist-generated merge=ours -diff\n/js/packages/store/protocol/grpc-web-gen/** linguist-generated merge=ours -diff\ngo.sum linguist-generated text\nyarn.lock linguist-generated text -diff\nPodfile.lock linguist-generated text -diff\npackage.json linguist-generated\ngo.mod text\n\n# Collapse snapshot files on GitHub\n*.snap linguist-generated\n\n# Reduce conflicts on markdown files\n*.md merge=union\n\n# specific for windows script files\n*.bat text eol=crlf\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_template.yml",
    "content": "# yamllint disable-line rule:document-start\nname: \"Bug report\"\ndescription: Report a bug found while using weshnet.\nlabels: [\"bug\"]\nbody:\n  - type: checkboxes\n    attributes:\n      label: Is there an existing issue for this?\n      description: >-\n        Please search to see if an issue already exists for the bug you\n        encountered.\n      options:\n        - label: I have searched the existing issues\n          required: true\n  - type: input\n    id: package-version\n    attributes:\n      label: Package version\n      description: What version of weshnet are you using?\n      placeholder: v1.0.3\n    validations:\n      required: true\n  - type: dropdown\n    id: os\n    attributes:\n      label: OS\n      description: What OS are you seeing the problem on?\n      options:\n        - iOS\n        - Android\n        - Linux\n        - macOS\n        - Windows\n        - Other\n  - type: input\n    id: language-version\n    attributes:\n      label: Language version and compiler version\n      description: What programming language version and compiler are you using?\n      placeholder: >-\n        go1.18.4, javac 11.0.12\n    validations:\n      required: true\n  - type: textarea\n    id: bug-description\n    attributes:\n      label: Bug description\n      description: Provide a bug description and a code snippet if applicable.\n      placeholder: |\n        1. Set up this environment ...\n        2. Add this code ...\n        3. Call this function ...\n    validations:\n      required: true\n  - type: textarea\n    id: current-behavior\n    attributes:\n      label: Current behavior\n      description: >-\n        Output after code execution including stack traces, debug logs, etc.\n      placeholder: Currently ...\n    validations:\n      required: true\n  - type: textarea\n    id: expected-behavior\n    attributes:\n      label: Expected behavior\n      description: Please provide what would be your expectation to happen.\n      placeholder: In this situation, weshnet should ...\n    validations:\n      required: true\n  - type: textarea\n    id: environment\n    attributes:\n      label: Environment\n      description: What is your development environment?\n      placeholder: macOS 13.1\n    validations:\n      required: true\n  - type: textarea\n    id: other\n    attributes:\n      label: Other\n      placeholder: Any other details?\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_template.yml",
    "content": "# yamllint disable-line rule:document-start\nname: \"Feature request\"\ndescription: Suggest an idea for this project.\nlabels: [\":rocket: feature-request\"]\nbody:\n  - type: checkboxes\n    attributes:\n      label: Is there an existing issue for this?\n      description: >-\n        Please search to see if an issue already exists for this feature\n        request.\n      options:\n        - label: I have searched the existing issues\n          required: true\n  - type: textarea\n    id: feature\n    attributes:\n      label: Feature request\n      description: >-\n        Provide a detailed description of the change or addition you are\n        proposing.\n      placeholder: There should be ...\n    validations:\n      required: true\n  - type: textarea\n    id: context\n    attributes:\n      label: Context\n      description: >-\n        Why is this change important to you? How would you use it? How can it\n        benefit other users?\n      placeholder: This feature request is important because ...\n    validations:\n      required: true\n  - type: textarea\n    id: implementation\n    attributes:\n      label: Possible implementation\n      description: >-\n        Not obligatory, but suggest an idea for implementing addition or change.\n      placeholder: This feature could be implemented by ...\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/question_template.yml",
    "content": "# yamllint disable-line rule:document-start\nname: \"Question\"\ndescription: Ask a question about this project.\nlabels: [\"question\"]\nbody:\n  - type: checkboxes\n    attributes:\n      label: Asking a question, not reporting a bug\n      description: >-\n        If your question is \"Why did I get this error?\" and you think it is a\n        bug, then please open a Bug Report at\n        https://github.com/berty/weshnet/issues/new/\n      options:\n        - label: This question is not about a bug\n          required: true\n  - type: checkboxes\n    attributes:\n      label: Is there an existing issue for this?\n      description: >-\n        Please search to see if an issue already exists for your question.\n      options:\n        - label: I have searched the existing issues\n          required: true\n  - type: textarea\n    id: question\n    attributes:\n      label: Question\n      description: >-\n        Provide your question with enough detail that it is helpful to anyone\n        reading the question (maybe years later).\n      placeholder: How do I ...\n    validations:\n      required: true\n  - type: textarea\n    id: context\n    attributes:\n      label: Context\n      description: >-\n        Is it a general question about the design and goals of weshnet, or\n        about how to use it in an app (or some other context)?\n      placeholder: This is a question about ...\n    validations:\n      required: true\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\n# @NOTE(gfanton): we use 0 as pull-request-limit to only enable security update.\n# see: https://docs.github.com/en/code-security/dependabot/dependabot-security-updates/configuring-dependabot-security-updates#overriding-the-default-behavior-with-a-configuration-file\nupdates:\n  - package-ecosystem: docker\n    directory: \"/tool/docker-protoc\"\n    schedule:\n      interval: daily\n    # Disable version updates for docker dependencies (enable only security update)\n    open-pull-requests-limit: 0\n    labels:\n      - \"security\"\n      - \"t/docker\"\n      - \"dependencies\"\n\n  - package-ecosystem: github-actions\n    directory: \"/\"\n    schedule:\n      interval: daily\n    # Disable version updates for github dependencies (enable only security update)\n    open-pull-requests-limit: 0\n    labels:\n      - \"security\"\n      - \"t/github-actions\"\n      - \"dependencies\"\n\n  - package-ecosystem: gomod\n    directory: \"/\"\n    schedule:\n      interval: daily\n    # Disable version updates for gomod dependencies (enable only security update)\n    open-pull-requests-limit: 0\n    labels:\n      - \"security\"\n      - \"t/golang\"\n      - \"dependencies\"\n"
  },
  {
    "path": ".github/pull_request_template.md",
    "content": "<!-- Thank you for your contribution! ❤️ -->\n"
  },
  {
    "path": ".github/weekly-digest.yml",
    "content": "# Configuration for weekly-digest - https://github.com/apps/weekly-digest\npublishDay: thu\ncanPublishIssues: true\ncanPublishPullRequests: true\ncanPublishContributors: true\ncanPublishStargazers: true\ncanPublishCommits: true\n"
  },
  {
    "path": ".github/workflows/benchmark.yml",
    "content": "name: Go benchmark\non:\n  push:\n    tags:\n      - v*\n    branches:\n      - main\n    paths:\n      - \"**\"\n      - \"!**.md\"\n      - \"go.*\"\n      - \"**.go\"\n      - \".github/workflows/benchmark.yml\"\n  pull_request:\n    paths:\n      - \"**\"\n      - \"!**.md\"\n      - \"go.*\"\n      - \"**.go\"\n      - \".github/workflows/benchmark.yml\"\n\njobs:\n  benchmark:\n    if: github.event_name == 'DISABLED'\n    name: Run benchmarks\n    runs-on: ubuntu-latest\n    steps:\n      - name: Setup asdf\n        uses: asdf-vm/actions/setup@9cd779f40fe38688dd19505ccbc4eaaf018b44e7\n        with:\n          asdf_version: 0.16.7\n\n      - name: Setup Graphviz\n        uses: ts-graphviz/setup-graphviz@v1\n\n      - name: Checkout\n        uses: actions/checkout@v3\n        with:\n          fetch-depth: 50 # this is to make sure we obtain the target base commit#\n\n      - name: Setup go\n        run: |\n          asdf plugin add golang\n          asdf install golang\n          echo \"go_version=$(asdf current golang | xargs | cut -d ' ' -f 2)\" >> $GITHUB_ENV\n          go install golang.org/x/perf/cmd/benchstat@latest\n          asdf reshim golang\n\n      - name: Cache go modules\n        uses: actions/cache@v4\n        with:\n          path: ~/go/pkg/mod\n          key: ${{ runner.os }}-go-${{ env.go_version }}-${{ env.json_cache-versions_go }}-${{ hashFiles('**/go.sum') }}\n          restore-keys: ${{ runner.os }}-go-${{ env.go_version }}-${{ env.json_cache-versions_go }}-\n\n      - name: Run benchmark\n        run: make go.unittest | tee output_head.txt\n        working-directory: .\n        env:\n          TEST_SPEED: any\n          GO_TEST_PATH: ./internal/benchmark\n          GO_TEST_OPTS: -bench=. -test.benchmem -cpuprofile cpu_head.prof -memprofile mem_head.prof -test.timeout=1200s -count=5\n\n      - name: Checkout base commit\n        run: git checkout ${{ github.event.pull_request.base.sha }}\n        if: github.event_name == 'pull_request'\n\n      - name: Cache go modules (main)\n        uses: actions/cache@v4\n        if: github.event_name == 'pull_request'\n        with:\n          path: ~/go/pkg/mod\n          key: ${{ runner.os }}-go-${{ env.go_version }}-${{ env.json_cache-versions_go }}-${{ hashFiles('**/go.sum') }}\n          restore-keys: ${{ runner.os }}-go-${{ env.go_version }}-${{ env.json_cache-versions_go }}-\n\n      - name: Run benchmark (main)\n        run: make go.unittest | tee output_base.txt\n        if: github.event_name == 'pull_request'\n        working-directory: .\n        env:\n          TEST_SPEED: any\n          GO_TEST_PATH: ./internal/benchmark\n          GO_TEST_OPTS: -bench=. -test.benchmem -cpuprofile cpu_base.prof -memprofile mem_base.prof -test.timeout=1200s -count=5\n\n      - name: Benchstat\n        id: benchstat-main\n        if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request'\n        run: |\n          echo 'Benchmark report' >> $GITHUB_STEP_SUMMARY\n          echo '```' >> $GITHUB_STEP_SUMMARY\n          benchstat output_head.txt >> $GITHUB_STEP_SUMMARY\n          echo '```' >> $GITHUB_STEP_SUMMARY\n        working-directory: .\n\n      - name: Benchstat PR\n        id: benchstat-pr\n        if: github.event_name == 'pull_request'\n        run: |\n          echo 'Benchmark report' >> $GITHUB_STEP_SUMMARY\n          echo '```' >> $GITHUB_STEP_SUMMARY\n          benchstat output_head.txt output_base.txt >> $GITHUB_STEP_SUMMARY\n          echo '```' >> $GITHUB_STEP_SUMMARY\n        working-directory: .\n\n      - name: Generate pprof html files\n        id: pprof-html-files-no-relative\n        working-directory: .\n        run: |\n          mkdir -p pprof_html/head/{cpu,mem}/{top,flamegraph,peek,source}\n\n          go tool pprof -http 0.0.0.0:9402 -no_browser ./cpu_head.prof < /dev/null & # https://github.com/google/pprof/issues/401#issuecomment-739576424\n          sleep 2\n          curl http://localhost:9402/ui/ > pprof_html/head/cpu/index.html\n          curl http://localhost:9402/ui/top > pprof_html/head/cpu/top/index.html\n          curl http://localhost:9402/ui/flamegraph > pprof_html/head/cpu/flamegraph/index.html\n          curl http://localhost:9402/ui/peek > pprof_html/head/cpu/peek/index.html\n          curl http://localhost:9402/ui/source > pprof_html/head/cpu/source/index.html\n          pkill pprof\n          sleep 2\n\n          go tool pprof -http 0.0.0.0:9402 -no_browser ./mem_head.prof < /dev/null &\n          sleep 2\n          curl http://localhost:9402/ui/ > pprof_html/head/mem/index.html\n          curl http://localhost:9402/ui/top > pprof_html/head/mem/top/index.html\n          curl http://localhost:9402/ui/flamegraph > pprof_html/head/mem/flamegraph/index.html\n          curl http://localhost:9402/ui/peek > pprof_html/head/mem/peek/index.html\n          curl http://localhost:9402/ui/source > pprof_html/head/mem/source/index.html\n          pkill pprof\n          sleep 2\n\n          cat << EOF > pprof_html/index.html\n          <!DOCTYPE html>\n          <html>\n          <head>\n          <meta charset=\"UTF-8\" />\n          <title>pprof output</title>\n          </head>\n          <body>\n          <h2>CPU output</h2>\n          <ul>\n            <li><a href=\"./head/cpu/\">Graph</a></li>\n            <li><a href=\"./head/cpu/top/\">Top</a></li>\n            <li><a href=\"./head/cpu/flamegraph/\">Flamegraph</a></li>\n            <li><a href=\"./head/cpu/peek/\">Peek</a></li>\n            <li><a href=\"./head/cpu/source/\">Source</a></li>\n          </ul>\n          <h2>Memory output</h2>\n          <ul>\n            <li><a href=\"./head/mem/\">Graph</a></li>\n            <li><a href=\"./head/mem/top/\">Top</a></li>\n            <li><a href=\"./head/mem/flamegraph/\">Flamegraph</a></li>\n            <li><a href=\"./head/mem/peek/\">Peek</a></li>\n            <li><a href=\"./head/mem/source/\">Source</a></li>\n          </ul>\n          EOF\n\n      - name: Generate pprof html files (PR)\n        if: github.event_name == 'pull_request'\n        id: pprof-html-files-pr\n        working-directory: .\n        run: |\n          mkdir -p pprof_html/base_comp/{cpu,mem}\n\n          go tool pprof -http 0.0.0.0:9402 --diff_base=./cpu_base.prof -no_browser ./cpu_head.prof < /dev/null &\n          sleep 2\n          curl http://localhost:9402/ui/ > pprof_html/base_comp/cpu/index.html\n          pkill pprof\n          sleep 2\n\n          go tool pprof -http 0.0.0.0:9402 --diff_base=./mem_base.prof -no_browser ./mem_head.prof < /dev/null &\n          sleep 2\n          curl http://localhost:9402/ui/ > pprof_html/base_comp/mem/index.html\n          pkill pprof\n          sleep 2\n\n          cat << EOF >> pprof_html/index.html\n          <h2>CPU diff against ${{ github.event.pull_request.base.sha }}</h2>\n          <ul>\n            <li><a href=\"./base_comp/cpu/\">Graph</a></li>\n          </ul>\n          <h2>Memory diff against ${{ github.event.pull_request.base.sha }}</h2>\n          <ul>\n            <li><a href=\"./base_comp/mem/\">Graph</a></li>\n          </ul>\n          EOF\n\n      - name: Generate pprof html files (footer)\n        id: pprof-html-files-footer\n        working-directory: .\n        run: |\n          cat << EOF >> pprof_html/index.html\n          </body>\n          </html>\n          EOF\n\n      # upload arifacts\n      #\n      - name: upload artifact (main)\n        uses: actions/upload-artifact@v3\n        if: github.ref == 'refs/heads/main'\n        with:\n          name: \"bench-main\"\n          path: go/pprof_html\n\n      - name: upload artifact (PR)\n        uses: actions/upload-artifact@v3\n        if: github.event_name == 'pull_request'\n        with:\n          name: \"bench-${{ github.event.pull_request.number  }}\"\n          path: go/pprof_html\n"
  },
  {
    "path": ".github/workflows/buf-push.yml",
    "content": "name: buf-push\non:\n  push:\n    branches:\n      - main\n\n# from https://docs.buf.build/ci-cd/github-actions#buf-push\njobs:\n  buf-release:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v2\n      - uses: bufbuild/buf-setup-action@v1\n      # @TODO(gfanton): enable this ?\n      # - uses: bufbuild/buf-lint-action@v1\n      # - uses: bufbuild/buf-breaking-action@v1\n      #   with:\n      #     # The 'main' branch of the GitHub repository that defines the module.\n      #     against: \"https://github.com/${GITHUB_REPOSITORY}.git#branch=main,ref=HEAD~1\"\n      - uses: bufbuild/buf-push-action@v1\n        with:\n          input: \"api/protocol\"\n          buf_token: ${{ secrets.BUF_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/cancel.yml",
    "content": "name: Cancel\non:\n  workflow_run:\n    workflows: [\n      \"CodeQL\",\n      \"Dependent Issues\",\n      \"Go\",\n      \"Integration\",\n      \"macOS Release\",\n      \"Protobuf\",\n    ]\n    types:\n      - requested\n\njobs:\n  cancel_pr_prev_push:\n    name: Cancel previous runs on PR update\n    if: ${{ github.event_name == 'workflow_run' }}\n    runs-on: ubuntu-latest\n    steps:\n    - uses: styfle/cancel-workflow-action@0.11.0\n      with:\n        workflow_id: ${{ github.event.workflow.id }}\n"
  },
  {
    "path": ".github/workflows/codeql-analysis.yml",
    "content": "# For most projects, this workflow file will not need changing; you simply need\n# to commit it to your repository.\n#\n# You may wish to alter this file to override the set of languages analyzed,\n# or to provide custom queries or build logic.\n#\n# ******** NOTE ********\n# We have attempted to detect the languages in your repository. Please check\n# the `language` matrix defined below to confirm you have the correct set of\n# supported CodeQL languages.\n#\nname: \"CodeQL\"\n\non:\n  push:\n    branches: [ main ]\n    paths:\n      - '**'\n      - '!**.md'\n      - 'go.*'\n      - '**.go'\n      - '.github/workflows/codeql-analysis.yml'\n  pull_request:\n    # The branches below must be a subset of the branches above\n    branches: [ main ]\n    paths:\n      - '**'\n      - '!**.md'\n      - 'go.*'\n      - '**.go'\n      - '.github/workflows/codeql-analysis.yml'\n\n  schedule:\n    - cron: '28 12 * * 4'\n\njobs:\n  analyze:\n    name: Analyze\n    runs-on: ubuntu-latest\n\n    strategy:\n      fail-fast: false\n      matrix:\n        language: [ 'go' ]\n        # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]\n        # Learn more:\n        # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed\n\n    steps:\n    - name: Checkout repository\n      uses: actions/checkout@v3\n\n    # Initializes the CodeQL tools for scanning.\n    - name: Initialize CodeQL\n      uses: github/codeql-action/init@v2\n      with:\n        languages: ${{ matrix.language }}\n        # If you wish to specify custom queries, you can do so here or in a config file.\n        # By default, queries listed here will override any specified in a config file.\n        # Prefix the list here with \"+\" to use these queries and those in the config file.\n        # queries: ./path/to/local/query, your-org/your-repo/queries@main\n\n    # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).\n    # If this step fails, then you should remove it and run the build manually (see below)\n    - name: Autobuild\n      uses: github/codeql-action/autobuild@v2\n\n    # ℹ️ Command-line programs to run using the OS shell.\n    # 📚 https://git.io/JvXDl\n\n    # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines\n    #    and modify them (or add more) to build your code if your project\n    #    uses a compiled language\n\n    #- run: |\n    #   make bootstrap\n    #   make release\n\n    - name: Perform CodeQL Analysis\n      uses: github/codeql-action/analyze@v2\n"
  },
  {
    "path": ".github/workflows/dependent-issues.yml",
    "content": "name: Dependent Issues\n\non:\n  issues:\n    types:\n      - opened\n      - edited\n      - reopened\n  pull_request_target:\n    types:\n      - opened\n      - edited\n      - reopened\n      - synchronize\n  schedule:\n    - cron: \"42 2 * * *\" # schedule daily check\n\njobs:\n  check:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: z0al/dependent-issues@v1.5.1\n        env:\n          # (Required) The token to use to make API calls to GitHub.\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        with:\n          # (Optional) The label to use to mark dependent issues\n          label: dependent\n\n          # (Optional) Enable checking for dependencies in issues. Enable by\n          # setting the value to \"on\". Default \"off\"\n          check_issues: off\n\n          # (Optional) A comma-separated list of keywords. Default\n          # \"depends on, blocked by\"\n          keywords: depends on, blocked by\n"
  },
  {
    "path": ".github/workflows/go.yml",
    "content": "name: Go\non:\n  push:\n    tags:\n      - v*\n    branches:\n      - main\n    paths:\n      - \"**\"\n      - \"!**.md\"\n      - \"go.*\"\n      - \"**.go\"\n      - \".github/workflows/go.yml\"\n  pull_request:\n    paths:\n      - \"**\"\n      - \"!**.md\"\n      - \"go.*\"\n      - \"**.go\"\n      - \".github/workflows/go.yml\"\n\njobs:\n  golangci-lint:\n    name: Golangci-lint\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n\n      - name: Setup asdf\n        uses: asdf-vm/actions/setup@9cd779f40fe38688dd19505ccbc4eaaf018b44e7\n        with:\n          asdf_version: 0.16.7\n\n      - name: Setup golang\n        run: |\n          asdf plugin add golang\n          asdf install golang\n\n      - name: Setup golangci-lint\n        run: |\n          asdf plugin add golangci-lint\n          asdf install golangci-lint\n\n      - name: Run golangci-lint\n        run: make lint\n\n  # this is not very common to have a job that checks the flappy tests.\n  #\n  # reason: some tests are flappy, they works, but not always;\n  #         this job checks that they are working sometimes.\n  #         if this job fails, then a test is \"broken\", not \"flappy\".\n  #\n  #         summary: this job checks that \"flappy tests\" do not become \"broken tests\".\n  #\n  # we hope we can remove this job because all the tests are stable 100% of the time\n  flappy-tests:\n    name: Flappy tests (Linux)\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v3\n\n      - name: Load variables from file\n        uses: antifree/json-to-variables@v1.0.1\n        with:\n          filename: .github/workflows/utils/variables.json\n\n      - name: Setup asdf\n        uses: asdf-vm/actions/setup@9cd779f40fe38688dd19505ccbc4eaaf018b44e7\n        with:\n          asdf_version: 0.16.7\n\n      - name: Setup go\n        run: |\n          asdf plugin add golang\n          asdf install golang\n          echo \"go_version=$(asdf current golang | xargs | cut -d ' ' -f 2)\" >> $GITHUB_ENV\n\n      - name: Cache go modules\n        uses: actions/cache@v4\n        with:\n          path: ~/go/pkg/mod\n          key: ${{ runner.os }}-go-${{ env.go_version }}-${{ env.json_cache-versions_go }}-${{ hashFiles('**/go.sum') }}\n          restore-keys: ${{ runner.os }}-go-${{ env.go_version }}-${{ env.json_cache-versions_go }}-\n\n      - name: Avoid triggering make generate\n        run: touch gen.sum\n\n      - name: Fetch go modules\n        run: go mod download\n\n      - name: Compile the testing binaries\n        run: |\n          pushd .  && go test -c -o ./tests.bin . && popd\n\n      - name: Check go.mod and go.sum\n        run: |\n          go mod tidy -v\n          git --no-pager diff go.mod go.sum\n          git --no-pager diff --quiet go.mod go.sum\n\n      - name: Run fast flappy tests\n        env:\n          TEST_SPEED: fast\n          TEST_STABILITY: flappy\n        run: make go.flappy-tests\n\n  go-tests-on-linux:\n    name: Stable tests (Linux)\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v3\n\n      - name: Load variables from file\n        uses: antifree/json-to-variables@v1.0.1\n        with:\n          filename: .github/workflows/utils/variables.json\n\n      - name: Setup asdf\n        uses: asdf-vm/actions/setup@9cd779f40fe38688dd19505ccbc4eaaf018b44e7\n        with:\n          asdf_version: 0.16.7\n\n      - name: Setup go\n        run: |\n          asdf plugin add golang\n          asdf install golang\n          echo \"go_version=$(asdf current golang | xargs | cut -d ' ' -f 2)\" >> $GITHUB_ENV\n\n      - name: Cache go modules\n        uses: actions/cache@v4\n        with:\n          path: ~/go/pkg/mod\n          key: ${{ runner.os }}-go-${{ env.go_version }}-${{ env.json_cache-versions_go }}-${{ hashFiles('**/go.sum') }}\n          restore-keys: ${{ runner.os }}-go-${{ env.go_version }}-${{ env.json_cache-versions_go }}-\n\n      - name: Check go.mod and go.sum\n        run: |\n          go mod tidy -v\n          git --no-pager diff go.mod go.sum\n          git --no-pager diff --quiet go.mod go.sum\n\n      - name: Run fast tests multiple times\n        env:\n          TEST_SPEED: fast\n          GO_TEST_OPTS: -test.timeout=600s -count 1\n        run: set -o pipefail; make go.unittest | tee test_log.txt\n\n      - name: Run all tests\n        env:\n          TEST_SPEED: any\n          GO_TEST_OPTS: -test.timeout=600s -count 1\n        run: make go.unittest\n\n      - name: Run all tests with race flag and generate coverage\n        env:\n          TEST_SPEED: any\n          GO_TEST_OPTS: -test.timeout=1200s -count=1 -race -cover  -coverprofile=coverage.txt -covermode=atomic\n        run: make go.unittest\n\n      - name: Upload coverage to Codecov\n        uses: codecov/codecov-action@v3.1.1\n        env:\n          OS: ${{ runner.os }}\n          GOLANG: ${{ env.go_version }}\n        with:\n          file: ./go/coverage.txt\n          flags: go.unittests\n          env_vars: OS,GOLANG\n          name: codecov-umbrella\n          fail_ci_if_error: false\n\n  go-tests-on-windows:\n    name: Stable tests (Windows)\n    runs-on: windows-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v3\n\n      - name: Load variables from file\n        uses: antifree/json-to-variables@v1.0.1\n        with:\n          filename: .github/workflows/utils/variables.json\n\n      - name: Get go version\n        shell: bash\n        run: echo \"go_version=$(cat .tool-versions | grep '^golang [0-9]\\+\\.[0-9]\\+\\.[0-9]\\+.*$' | cut -d ' ' -f 2)\" >> $GITHUB_ENV\n\n      - name: Setup go\n        uses: actions/setup-go@v3\n        with:\n          go-version: ${{ env.go_version }}\n\n      - name: Cache go modules\n        uses: actions/cache@v4\n        with:\n          path: ~/go/pkg/mod\n          key: ${{ runner.os }}-go-${{ env.go_version }}-${{ env.json_cache-versions_go }}-${{ hashFiles('**/go.sum') }}\n          restore-keys: ${{ runner.os }}-go-${{ env.go_version }}-${{ env.json_cache-versions_go }}-\n\n      - name: Check go.mod and go.sum\n        run: |\n          go mod tidy -v\n          git --no-pager diff go.mod go.sum\n          git --no-pager diff --quiet go.mod go.sum\n\n      - name: Run fast tests multiple times\n        env:\n          TEST_SPEED: fast\n        run: go.exe test ./...  -buildmode=exe -timeout=600s -count=5\n\n      - name: Run all tests\n        env:\n          TEST_SPEED: any\n        run: go.exe test ./...  -buildmode=exe -timeout=600s -count=1\n\n  go-tests-on-macos:\n    name: Stable tests (macOS)\n    runs-on: macos-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v3\n\n      - name: Load variables from file\n        uses: antifree/json-to-variables@v1.0.1\n        with:\n          filename: .github/workflows/utils/variables.json\n\n      - name: Setup asdf\n        uses: asdf-vm/actions/setup@9cd779f40fe38688dd19505ccbc4eaaf018b44e7\n        with:\n          asdf_version: 0.16.7\n\n      - name: Setup go\n        run: |\n          asdf plugin add golang\n          asdf install golang\n          echo \"go_version=$(asdf current golang | xargs | cut -d ' ' -f 2)\" >> $GITHUB_ENV\n\n      - name: Cache go modules\n        uses: actions/cache@v4\n        with:\n          path: ~/go/pkg/mod\n          key: ${{ runner.os }}-go-${{ env.go_version }}-${{ env.json_cache-versions_go }}-${{ hashFiles('**/go.sum') }}\n          restore-keys: ${{ runner.os }}-go-${{ env.go_version }}-${{ env.json_cache-versions_go }}-\n\n      - name: Check go.mod and go.sum\n        run: |\n          go mod tidy -v\n          git --no-pager diff go.mod go.sum\n          git --no-pager diff --quiet go.mod go.sum\n\n      - name: Run fast tests multiple times\n        env:\n          TEST_SPEED: fast\n          GO_TEST_OPTS: -test.timeout=600s -count 1\n        run: set -o pipefail; make go.unittest | tee test_log.txt\n\n      - name: Run all tests\n        env:\n          TEST_SPEED: any\n          GO_TEST_OPTS: -test.timeout=600s -count 1\n        run: make go.unittest\n\n      - name: Run all tests with race flag and generate coverage\n        env:\n          TEST_SPEED: any\n          GO_TEST_OPTS: -test.timeout=1200s -count=1 -race -cover  -coverprofile=coverage.txt -covermode=atomic\n        run: make go.unittest\n\n      - name: Upload coverage to Codecov\n        uses: codecov/codecov-action@v3.1.1\n        env:\n          OS: ${{ runner.os }}\n          GOLANG: ${{ env.go_version }}\n        with:\n          file: ./go/coverage.txt\n          flags: go.unittests\n          env_vars: OS,GOLANG\n          name: codecov-umbrella\n          fail_ci_if_error: false\n\n  # TODO: consider adding various GOARCH check per OS.\n  #       i.e., to validate that we build on 32/64bit.\n"
  },
  {
    "path": ".github/workflows/protobuf.yml",
    "content": "name: Protobuf\non:\n  push:\n    tags:\n      - v*\n    branches:\n      - main\n    paths:\n      - \"api/**\"\n      - \"Makefile\"\n      - \"docs/Makefile\"\n      - \".github/workflows/protobuf.yml\"\n      - \"**/gen.sum\"\n      - \"**.pb.go\"\n      - \"**.gen.go\"\n      - \"**.gen.graphql\"\n      - \"**.gen.yml\"\n      - \"**.pb.go\"\n      - \"**/pb_test.go\"\n      - \"**/docs/*/api.md\"\n      - \"**/go.mod\"\n      - \"**/go.sum\"\n  pull_request:\n    paths:\n      - \"api/**\"\n      - \"Makefile\"\n      - \"docs/Makefile\"\n      - \".github/workflows/protobuf.yml\"\n      - \"**/gen.sum\"\n      - \"**.pb.go\"\n      - \"**.gen.go\"\n      - \"**.gen.graphql\"\n      - \"**.gen.yml\"\n      - \"**.pb.go\"\n      - \"**/pb_test.go\"\n      - \"**/docs/*/api.md\"\n      - \"**/go.mod\"\n      - \"**/go.sum\"\njobs:\n  gen-go-and-docs:\n    if: github.event_name == 'DISABLED' # need to fix it by removing docker for generation\n    name: Generate go protobuf and docs\n    runs-on: ubuntu-latest\n    container: bertytech/buf:1\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v3\n\n      - name: Unshallow\n        run: git fetch --prune --unshallow\n\n      - name: Remove lock files\n        run: find . -name gen.sum -delete\n\n      - name: Load variables from file\n        uses: antifree/json-to-variables@v1.0.1\n        with:\n          filename: .github/workflows/utils/variables.json\n\n      - name: Setup asdf\n        uses: asdf-vm/actions/setup@9cd779f40fe38688dd19505ccbc4eaaf018b44e7\n        with:\n          asdf_version: 0.16.7\n\n      - name: Setup go\n        run: |\n          asdf plugin add golang\n          asdf install golang\n          echo \"go_version=$(asdf current golang | xargs | cut -d ' ' -f 2)\" >> $GITHUB_ENV\n\n      - name: Setup jq\n        run: |\n          asdf plugin add jq\n          asdf install jq\n\n      - name: Cache go modules\n        uses: actions/cache@v4\n        with:\n          path: ~/go/pkg/mod\n          key: ${{ runner.os }}-go-${{ env.go_version }}-${{ env.json_cache-versions_go }}-${{ hashFiles('**/go.sum') }}\n          restore-keys: ${{ runner.os }}-go-${{ env.go_version }}-${{ env.json_cache-versions_go }}-\n\n      - name: Fetch go modules\n        run: go mod download\n\n      - name: Generate docs\n        working-directory: docs\n        run: make generate_local\n\n      - name: Generate go protobuf\n        run: |\n          make generate_local\n          git checkout go.mod go.sum\n\n      - name: Check diff\n        run: |\n          git status | cat\n          git diff -w | cat\n          git diff-index -w --quiet HEAD --\n\n      - name: Prepare openapi documentation\n        working-directory: docs\n        run: make openapi.prepare\n\n      - name: Setup apiary\n        run: apk --no-cache add ruby-dev g++ && gem install apiaryio\n\n      - name: Upload API docs to apiary.io\n        env:\n          APIARY_API_KEY: \"${{ secrets.APIARY_API_KEY }}\"\n        if: ${{ env.APIARY_API_KEY != 0 }}\n        run: |\n          apiary publish --api-name=bertyprotocol  --path=\"docs/.tmp/openapi/bertyprotocol.swagger.json\"  || true\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: Release\non:\n  push:\n    branches:\n      - main\n  pull_request:\n    paths:\n      # Go\n      - \"**\"\n      - \"!**.md\"\n      - \".goreleaser\"\n      - \"go.*\"\n      - \"**.go\"\n      # CI\n      - \".github/workflows/release.yml\"\n\njobs:\n  semantic-release:\n    name: Semantic release\n    runs-on: ubuntu-latest\n    outputs:\n      new-release-published: ${{ steps.semantic-echo.outputs.new-release-published }}\n      release-version: ${{ steps.semantic-echo.outputs.release-version }}\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v3\n\n      - name: Unshallow\n        run: git fetch --prune --unshallow\n\n      - name: Run Semantic Release\n        id: semantic\n        uses: docker://ghcr.io/codfish/semantic-release-action:v1\n        with:\n          branches: |\n            ['main']\n          plugins: |\n            [\n              '@semantic-release/commit-analyzer',\n              '@semantic-release/release-notes-generator',\n              '@semantic-release/github'\n            ]\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n\n      - name: Export Semantic Release\n        id: semantic-echo\n        run: |\n          echo \"::set-output name=new-release-published::${{steps.semantic.outputs.new-release-published}}\"\n          echo \"::set-output name=release-version::${{steps.semantic.outputs.release-version}}\"\n\n  post-semantic-release:\n    needs: semantic-release\n    #if: needs.semantic-release.outputs.new-release-published == 'true'\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v3\n\n      - name: Unshallow\n        run: git fetch --prune --unshallow\n\n      - name: Setup asdf\n        uses: asdf-vm/actions/setup@9cd779f40fe38688dd19505ccbc4eaaf018b44e7\n        with:\n          asdf_version: 0.16.7\n\n      - name: Setup go\n        run: |\n          asdf plugin add golang\n          asdf install golang\n\n      - name: Register version on pkg.go.dev\n        if: needs.semantic-release.outputs.new-release-published == 'true'\n        run: |\n          package=$(cat go.mod | grep ^module | awk '{print $2}')\n          version=v${{ needs.semantic-release.outputs.release-version }}\n          url=https://proxy.golang.org/${package}/@v/${version}.info\n          set -x +e\n          curl -i $url\n"
  },
  {
    "path": ".github/workflows/ssh-runner.yml",
    "content": "name: SSH on runner\non:\n  workflow_dispatch:\n    inputs:\n      os:\n        description: \"Operating System\"\n        required: true\n        default: ubuntu-latest\n        type: choice\n        options:\n          - ubuntu-latest\n          - macos-latest\n          - windows-latest\n      mod:\n        description: \"Install Go/Node modules\"\n        required: true\n        default: true\n        type: boolean\n\njobs:\n  setup-ssh:\n    name: Setup runner and open SSH endpoint\n    runs-on: ${{ github.event.inputs.os }}\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v3\n        with:\n          fetch-depth: 0\n          persist-credentials: false\n\n      - name: Load variables from file\n        uses: antifree/json-to-variables@v1.0.1\n        with:\n          filename: .github/workflows/utils/variables.json\n\n      - name: Setup asdf\n        uses: asdf-vm/actions/setup@9cd779f40fe38688dd19505ccbc4eaaf018b44e7\n        with:\n          asdf_version: 0.16.7\n\n      - name: Setup go\n        if: runner.os != 'Windows'\n        run: |\n          asdf plugin add golang\n          asdf install golang\n          echo \"go_version=$(asdf current golang | xargs | cut -d ' ' -f 2)\" >> $GITHUB_ENV\n\n      - name: Cache go modules\n        if: github.event.inputs.mod == 'true' && runner.os != 'Windows'\n        uses: actions/cache@v4\n        with:\n          path: ~/go/pkg/mod\n          key: ${{ runner.os }}-go-${{ env.go_version }}-${{ env.json_cache-versions_go }}-${{ hashFiles('**/go.sum') }}\n          restore-keys: ${{ runner.os }}-go-${{ env.go_version }}-${{ env.json_cache-versions_go }}-\n\n      - name: Fetch go modules\n        if: github.event.inputs.mod == 'true' && runner.os != 'Windows'\n        working-directory: .\n        run: go mod tidy\n\n      - name: Install emacs\n        shell: bash\n        run: |\n          if [ \"$RUNNER_OS\" == \"Linux\" ]; then\n              sudo apt-get install -y emacs\n          elif [ \"$RUNNER_OS\" == \"Windows\" ]; then\n              choco install emacs\n          else\n              echo \"Already installed!\"\n          fi\n\n      - name: Setup tmate session\n        uses: mxschmitt/action-tmate@v3\n        with:\n          limit-access-to-actor: true\n"
  },
  {
    "path": ".github/workflows/utils/variables.json",
    "content": "{\n  \"cache-versions\": {\n    \"go\": \"1\"\n  }\n}\n"
  },
  {
    "path": ".gitignore",
    "content": "*#\n*~\n.#*\n.DS_Store\n.agignore\n.env\n.projectile\n.tmp/\n.idea/\n*.bin\n*.swp\n.vscode\ncore-sources.jar\ncoverage.txt\ndist/\ngen.sum.tmp\ngin-bin\nout/\nprofile.out\nvendor/\ndebug.log\n\nbenchmark_result.json\n\n# Delve's binaries\n*__debug_bin\n\n# Dev dbs\n/*gui.d\n"
  },
  {
    "path": ".golangci.yml",
    "content": "run:\n  deadline: 1m\n  tests: false\nissues:\n  exclude-files:\n    - \".*\\\\.pb\\\\.go$\"\n    - \".*\\\\.pb\\\\.gw\\\\.go$\"\n    - \".*\\\\.gen\\\\.go$\"\n    - \"_test\\\\.go$\"\n    - \"testing.go$\"\n    - \".*doc\\\\.go$\"\n\nlinters-settings:\n  golint:\n    min-confidence: 0\n  maligned:\n    suggest-new: true\n  misspell:\n    locale: US\n  gci:\n    sections:\n      - standard\n      - default\n      - prefix(berty.tech)\n      # - prefix(github.com/libp2p)\n      # - prefix(github.com/ipfs)\n\nlinters:\n  disable-all: true\n  enable:\n    - asciicheck\n    - bodyclose\n    #- depguard\n    - dogsled\n    - errcheck\n    #- exhaustive    # nice to have\n    - exportloopref\n    - gci\n    - gochecknoinits\n    #- gocognit      # nice to have\n    #- goconst\n    - gocritic\n    #- godot         # nice to have\n    #- goerr113      # nice to have\n    - gofmt\n    - gofumpt\n    - goimports\n    - revive\n    #- gomnd         # nice to have\n    - gomodguard\n    - gosec\n    - gosimple\n    - govet\n    - ineffassign\n    - misspell\n    - nakedret\n    - noctx\n    #- nolintlint\n    - exportloopref\n    - staticcheck\n    - typecheck\n    - unconvert\n    - unparam\n    - unused\n    - whitespace\n"
  },
  {
    "path": ".tool-versions",
    "content": "# To see the date when a version was updated, use git blame:\n# https://github.com/berty/weshnet/blame/main/.tool-versions\n\n#-----\n# This is simply the most recent version available to date of the lowest\n# major version of Go which is allowed by kubo.\n# There is no contraindication for updating it.\n#-----\ngolang 1.22.5\n\n#-----\n# This is simply the most recent golangci-lint version available to date.\n#-----\ngolangci-lint 1.59.1\n\n#-----\n# This is simply the most recent jq version available to date.\n# There is no contraindication for updating it.\n#-----\njq 1.6\n\n#-----\n# This is simply the most recent buf version available to date.\n# There is no contraindication for updating it.\n#-----\nbuf 1.39.0\n"
  },
  {
    "path": "COPYRIGHT",
    "content": "Copyright 2018-2023 Berty Technologies and other Berty Developers.\n\nIntellectual Property Notice\n----------------------------\n\nBerty is licensed under the Apache License, Version 2.0\n(see LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) or\nthe MIT license (see LICENSE-MIT or http://opensource.org/licenses/MIT),\nat your option.\n\nCopyrights and patents in the Berty project are retained\nby contributors.\nNo copyright assignment is required to contribute to Berty\n\n    SPDX-License-Identifier: (Apache-2.0 OR MIT)\n\nSPDX usage\n----------\n\nIndividual files may contain SPDX tags instead of the full license text.\nThis enables machine processing of license information based on the SPDX\nLicense Identifiers that are available here: https://spdx.org/licenses/\n"
  },
  {
    "path": "INSTALL.md",
    "content": "# Build weshnet\n\nThese are instructions to build weshnet.\n\n<!-- markdownlint-disable MD034 -->\n\n## Prerequisites\n\n* Required: asdf\n* Required on macOS: Command Line Developer Tools\n\nFollowing are the steps to install each prerequisite (if it's needed for your\nbuild target).\n\n### macOS 14, macOS 15 and macOS 26\n\nTo install the Command Line Developer Tools, in a terminal enter:\n\n    xcode-select --install\n\nTo install asdf using brew, follow instructions at https://asdf-vm.com . In short,\nfirst install brew following the instructions at https://brew.sh . Then, in\na terminal enter:\n\n    brew install asdf gpg\n\nIf your terminal is zsh, enter:\n\n    echo -e \"\\n. $(brew --prefix asdf)/libexec/asdf.sh\" >> ${ZDOTDIR:-~}/.zshrc\n\nIf your terminal is bash, enter:\n\n    echo -e \"\\n. \\\"$(brew --prefix asdf)/libexec/asdf.sh\\\"\" >> ~/.bash_profile\n\nStart a new terminal to get the changes to .zshrc .\n\n### Ubuntu 18.04, 20.04, 22.04 and 24.04\n\nTo install asdf, follow instructions at https://asdf-vm.com . In short, in\na terminal enter:\n\n    sudo apt install curl git build-essential\n    git clone https://github.com/asdf-vm/asdf.git ~/.asdf\n    echo '. \"$HOME/.asdf/asdf.sh\"' >> ~/.bashrc\n\nStart a new terminal to get the changes to .bashrc .\n\n## Build\n\nIn a terminal, enter:\n\n    git clone https://github.com/berty/weshnet\n    cd weshnet\n\nFirst time only (or after updating .tool-versions), enter:\n\n    make asdf.install_tools\n\nTo run the tests, enter:\n\n    make test\n\nOr you can make other targets. See:\n\n    make help\n"
  },
  {
    "path": "LICENSE-APACHE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright 2018-2021 Berty Technologies\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "LICENSE-MIT",
    "content": "Copyright (c) 2018-2021 Berty Technologies\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "Makefile",
    "content": "##\n## Config\n##\n\nGO ?= go\nGOPATH ?= $(HOME)/go\nGO_TAGS ?= -tags \"\"\nGO_TEST_OPTS ?= -test.timeout=300s -race -cover -coverprofile=coverage.txt -covermode=atomic $(GO_TAGS)\nGO_TEST_PATH ?= ./...\nGO_TEST_ENV ?=\nCI ?= false\n\nBUILD_DATE ?= `date +%s`\nVCS_REF ?= `git rev-parse --short HEAD`\nVERSION ?= `go run github.com/mdomke/git-semver/v5`\nLDFLAGS ?= -ldflags=\"-X berty.tech/weshnet/internal/bertyversion.VcsRef=$(VCS_REF) -X berty.tech/weshnet/internal/bertyversion.Version=$(VERSION)\"\n\n# @FIXME(gfanton): on macOS Monterey (12.0.x) we currently need to set the\n# environment variable `MallocNanoZone` to 0 to avoid a SIGABRT or SIGSEGV\n# see https://github.com/golang/go/issues/49138\nMACOS_VERSION=$(shell defaults read /System/Library/CoreServices/SystemVersion.plist 'ProductVersion' 2>/dev/null | sed 's/\\.[0-9]$$//')\nifeq ($(MACOS_VERSION),12.0)\nGO_TEST_ENV := MallocNanoZone=0 $(GO_TEST_ENV)\nendif\n\nifeq ($(MACOS_VERSION),12.1)\nGO_TEST_ENV := MallocNanoZone=0 $(GO_TEST_ENV)\nendif\n\n##\n## General rules\n##\n\nall: help\n.PHONY: all\n\n\nhelp:\n\t@echo \"Available make commands:\"\n\t@cat Makefile | grep '^[a-z]' | grep -v '=' | cut -d: -f1 | sort | sed 's/^/  /'\n.PHONY: help\n\n\ntest: unittest lint tidy\n.PHONY: test\n\n\nunittest: go.unittest\n.PHONY: unittest\n\n\ngenerate: pb.generate docs.generate\n.PHONY: generate\n\n\nregenerate: gen.clean docs.clean generate docs.generate\n.PHONY: regenerate\n\n\nclean: gen.clean docs.clean\n\trm -rf out/\n.PHONY: clean\n\n\nre: clean generate\n.PHONY: re\n\n\ntidy: go.tidy\n.PHONY: tidy\n\n\nlint: go.lint\n.PHONY: lint\n\n\nlint.fix: go.fmt\n.PHONY: lint.fix\n\n##\n## Other rules\n##\n\n\ncheck-program = $(foreach exec,$(1),$(if $(shell PATH=\"$(PATH)\" which $(exec)),,$(error \"No $(exec) in PATH\")))\n\ngo.tidy: pb.generate\n\t$(call check-program, $(GO))\n\tGO111MODULE=on $(GO) mod tidy\n.PHONY: go.tidy\n\ngo.lint: pb.generate\n\t$(call check-program, golangci-lint)\n\tgolangci-lint run --timeout=5m $(if $(filter $(CI), false), --verbose) ./...\n.PHONY: go.lint\n\ngo.unittest: pb.generate\n\t$(call check-program, $(GO))\n\t$(GO_TEST_ENV) GO111MODULE=on $(GO) test $(GO_TEST_OPTS) $(GO_TEST_PATH)\n.PHONY: go.unittest\n\n\ngo.flappy-tests: pb.generate\n\tTEST_STABILITY=flappy go run moul.io/testman test -v -test.v -timeout=600s -retry=10 -run ^TestFlappy    ./\n\tTEST_STABILITY=flappy go run moul.io/testman test -v -test.v -timeout=600s -retry=10 -run ^TestScenario_ ./\n\tTEST_STABILITY=flappy go run moul.io/testman test -v -test.v -timeout=600s -retry=10 -run ^TestFlappy    ./pkg/tinder\n\t# FIXME: run on other packages too\n.PHONY: go.flappy-tests\n\n\ngo.broken-tests: pb.generate\n\tTEST_STABILITY=broken go run moul.io/testman test -continue-on-error -timeout=1200s -test.timeout=60s -retry=5 -run ^TestScenario_ ./\n.PHONY: go.broken-tests\n\n\nprint-%:\n\t@echo $* = $($*)\n\n\nminimum_go_minor_version = 14\nvalidate-go-version:\n\t@if [ ! \"x`$(GO) version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f1`\" = \"x1\" ]; then \\\n\t\techo \"error: Golang version should be \\\"1.x\\\". Please use 1.$(minimum_go_minor_version) or more recent.\"; \\\n\t\texit 1; \\\n\tfi\n\t@if [ `$(GO) version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f2` -lt $(minimum_go_minor_version) ]; then \\\n\t\techo \"error: Golang version is not supported. Please use 1.$(minimum_go_minor_version) or more recent.\"; \\\n\t\texit 1; \\\n\tfi\n.PHONY: validate-go-version\n\n\n##\n## Code gen\n##\n\n\nprotos_src := $(wildcard ../api/*.proto) $(wildcard ../api/go-internal/*.proto)\ngen_src := $(protos_src) Makefile\ngen_sum := gen.sum\nprotoc_opts := -I ../api:`go list -m -mod=mod -f {{.Dir}} github.com/grpc-ecosystem/grpc-gateway`/third_party/googleapis:`go list -m -mod=mod -f {{.Dir}} github.com/gogo/protobuf`:/protobuf\npb.generate: gen.sum validate-go-version\n$(gen_sum): $(gen_src)\n\t$(call check-program, shasum docker $(GO))\n\t@shasum $(gen_src) | sort -k 2 > $(gen_sum).tmp\n\t@diff -q $(gen_sum).tmp $(gen_sum) || ( \\\n\t  uid=`id -u`; \\\n\t  set -xe; \\\n\t  $(GO) mod download; \\\n\t  docker run \\\n\t\t--user=\"$$uid\" \\\n\t\t--volume=\"`go env GOPATH`/pkg/mod:/go/pkg/mod\" \\\n\t\t--volume=\"$(PWD):/go/src/berty.tech/weshnet\" \\\n\t\t--workdir=\"/go/src/berty.tech/weshnet\" \\\n\t\t--entrypoint=\"sh\" \\\n\t\t--rm \\\n\t\tbertytech/buf:5 \\\n\t\t-xec 'make generate_local'; \\\n\t  $(MAKE) tidy \\\n\t)\n.PHONY: pb.generate\n\n\ngenerate_local:\n\tgo version\n\t$(call check-program, shasum buf)\n\tbuf generate api/go-internal;\n\tbuf generate api/protocol;\n\tbuf generate --template buf.gen.tag.yaml api/go-internal;\n\tbuf generate --template buf.gen.tag.yaml api/protocol;\n\t$(MAKE) go.fmt\n\tshasum $(gen_src) | sort -k 2 > $(gen_sum).tmp\n\tmv $(gen_sum).tmp $(gen_sum)\n.PHONY: generate_local\n\n\ngo.fmt:\n\tgo run github.com/daixiang0/gci write . \\\n\t\t--skip-generated -s 'standard,default,prefix(berty.tech)'\n\tgo run mvdan.cc/gofumpt -w .\n\n.PHONY: go.fmt\n\npkger.generate:\n\t$(GO) run github.com/markbates/pkger/cmd/pkger -o go/pkg/assets/\n.PHONY: pkger.generate\n\ngen.clean:\n\trm -f gen.sum $(wildcard */*/*.pb.go) $(wildcard */*/*pb_test.go) $(wildcard */*/*pb.gw.go)\n.PHONY: gen.clean\n\npb.push:\n\tbuf push api/protocol\n\n##\n## Docs gen\n##\n\n\ndocs.generate:\n\tcd docs; $(MAKE) generate\n.PHONY: docs.generate\n\ndocs.clean:\n\tcd docs; $(MAKE) clean\n.PHONY: docs.generate\n\n\nasdf.install_plugins:\n\t$(call check-program, asdf)\n\t@echo \"Installing asdf plugins...\"\n\t@set -e; \\\n\tfor PLUGIN in $$(cut -d' ' -f1 .tool-versions | grep \"^[^\\#]\"); do \\\n\t\tasdf plugin add $$PLUGIN || [ $$?==2 ] || exit 1; \\\n\tdone\n.PHONY: asdf.install_plugins\n\nasdf.install_tools: asdf.install_plugins\n\t$(call check-program, asdf)\n\t@echo \"Installing asdf tools...\"\n\t@asdf install\n.PHONY: asdf.install_tools\n"
  },
  {
    "path": "README.md",
    "content": "# Wesh Network Toolkit\n\n[![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white)](https://pkg.go.dev/berty.tech/weshnet)\n\nThe Wesh network toolkit lets your application use the\n[Wesh protocol](https://berty.tech/docs/protocol) to support privacy-based, off-grid, peer-to-peer communication.\nWesh powers [Berty Messenger](https://github.com/berty/berty#readme), and now you can use\nthe Wesh network toolkit directly.\n\nYour application interfaces to Wesh based on [gRPC](https://grpc.io). So even though\nthe core Wesh code is written in Go, Wesh works with your application written in Go, Python\nor [other languages](https://grpc.io/docs/languages) supported by gRPC.\n\nFor details, see the Wesh website at https://wesh.network .\nThe website includes [blog tutorials](https://wesh.network/blog) which introduce you to\nWesh and walk you through some example applications and background of the Wesh protocol. \n\n## Usage\n\n```go\nimport \"berty.tech/weshnet\"\n```\n\nOnline API documentation is at https://buf.build/berty-technologies/weshnet .\n\n## Get the code\n\nTo get the code and build, see the file\n[INSTALL.md](https://github.com/berty/weshnet/blob/master/INSTALL.md).\n\n## Feedback\n\nFor bug reports, feature requests or questions, please open a\n[GitHub issue](https://github.com/berty/weshnet/issues/new/choose).\n\n<!--\n## Examples\n\n_TODO: add links to internal examples + links to external repos using the protocol_\n-->\n"
  },
  {
    "path": "account_export.go",
    "content": "package weshnet\n\nimport (\n\t\"archive/tar\"\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n\n\t\"github.com/ipfs/go-cid\"\n\tcbornode \"github.com/ipfs/go-ipld-cbor\"\n\tcoreiface \"github.com/ipfs/kubo/core/coreiface\"\n\tmh \"github.com/multiformats/go-multihash\"\n\t\"go.uber.org/multierr\"\n\t\"go.uber.org/zap\"\n\t\"google.golang.org/protobuf/proto\"\n\n\torbitdb \"berty.tech/go-orbit-db\"\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n)\n\nconst (\n\texportAccountKeyFilename      = \"account.key\"\n\texportAccountProofKeyFilename = \"account_proof.key\"\n\texportOrbitDBEntriesPrefix    = \"entries/\"\n\texportOrbitDBHeadsPrefix      = \"heads/\"\n)\n\nfunc (s *service) export(ctx context.Context, output io.Writer) error {\n\ttw := tar.NewWriter(output)\n\tdefer tw.Close()\n\n\tif err := s.exportAccountKeys(tw); err != nil {\n\t\treturn errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\n\ts.lock.RLock()\n\tgroups := make([]*GroupContext, len(s.openedGroups))\n\ti := 0\n\tfor _, gc := range s.openedGroups {\n\t\tgroups[i] = gc\n\t\ti++\n\t}\n\ts.lock.RUnlock()\n\n\tfor _, gc := range groups {\n\t\tif err := s.exportGroupContext(ctx, gc, tw); err != nil {\n\t\t\treturn errcode.ErrCode_ErrInternal.Wrap(err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (s *service) exportGroupContext(ctx context.Context, gc *GroupContext, tw *tar.Writer) error {\n\tif err := s.exportOrbitDBStore(ctx, gc.metadataStore, tw); err != nil {\n\t\treturn errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\n\tif err := s.exportOrbitDBStore(ctx, gc.messageStore, tw); err != nil {\n\t\treturn errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\n\tmetaRawHeads := gc.metadataStore.OpLog().RawHeads()\n\tcidsMeta := make([]cid.Cid, metaRawHeads.Len())\n\tfor i, raw := range metaRawHeads.Slice() {\n\t\tcidsMeta[i] = raw.GetHash()\n\t}\n\n\tmessagesRawHeads := gc.messageStore.OpLog().RawHeads()\n\tcidsMessages := make([]cid.Cid, messagesRawHeads.Len())\n\tfor i, raw := range messagesRawHeads.Slice() {\n\t\tcidsMessages[i] = raw.GetHash()\n\t}\n\n\tif err := s.exportOrbitDBGroupHeads(gc, cidsMeta, cidsMessages, tw); err != nil {\n\t\treturn errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\n\treturn nil\n}\n\nfunc (s *service) exportOrbitDBStore(ctx context.Context, store orbitdb.Store, tw *tar.Writer) error {\n\tallCIDs := store.OpLog().GetEntries().Keys()\n\n\tif len(allCIDs) == 0 {\n\t\treturn nil\n\t}\n\n\tfor _, idStr := range allCIDs {\n\t\tif err := s.exportOrbitDBEntry(ctx, tw, idStr); err != nil {\n\t\t\tif clErr := tw.Close(); clErr != nil {\n\t\t\t\terr = multierr.Append(err, clErr)\n\t\t\t}\n\n\t\t\treturn errcode.ErrCode_ErrInternal.Wrap(err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (s *service) exportAccountKeys(tw *tar.Writer) error {\n\taccountPrivateKeyBytes, accountProofPrivateKeyBytes, err := s.secretStore.ExportAccountKeysForBackup()\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\n\terr = exportPrivateKey(tw, accountPrivateKeyBytes, exportAccountKeyFilename)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrStreamWrite.Wrap(err)\n\t}\n\n\terr = exportPrivateKey(tw, accountProofPrivateKeyBytes, exportAccountProofKeyFilename)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrStreamWrite.Wrap(err)\n\t}\n\n\treturn nil\n}\n\nfunc (s *service) exportOrbitDBGroupHeads(gc *GroupContext, headsMetadata []cid.Cid, headsMessages []cid.Cid, tw *tar.Writer) error {\n\tcidsMeta := make([][]byte, len(headsMetadata))\n\tfor i, id := range headsMetadata {\n\t\tcidsMeta[i] = id.Bytes()\n\t}\n\n\tcidsMessages := make([][]byte, len(headsMessages))\n\tfor i, id := range headsMessages {\n\t\tcidsMessages[i] = id.Bytes()\n\t}\n\n\tspk, err := gc.group.GetSigningPubKey()\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\tspkBytes, err := spk.Raw()\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\tlinkKeyArr, err := gc.group.GetLinkKeyArray()\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\theadsExport := &protocoltypes.GroupHeadsExport{\n\t\tPublicKey:         gc.group.PublicKey,\n\t\tSignPub:           spkBytes,\n\t\tMetadataHeadsCids: cidsMeta,\n\t\tMessagesHeadsCids: cidsMessages,\n\t\tLinkKey:           linkKeyArr[:],\n\t}\n\n\tentryName := base64.RawURLEncoding.EncodeToString(gc.group.PublicKey)\n\n\tdata, err := proto.Marshal(headsExport)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\tif err := tw.WriteHeader(&tar.Header{\n\t\tTypeflag: tar.TypeReg,\n\t\tName:     fmt.Sprintf(\"%s%s\", exportOrbitDBHeadsPrefix, entryName),\n\t\tMode:     0o600,\n\t\tSize:     int64(len(data)),\n\t}); err != nil {\n\t\treturn errcode.ErrCode_ErrStreamWrite.Wrap(err)\n\t}\n\n\tsize, err := tw.Write(data)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrStreamWrite.Wrap(err)\n\t}\n\n\tif size != len(data) {\n\t\treturn errcode.ErrCode_ErrStreamWrite.Wrap(fmt.Errorf(\"wrote %d bytes instead of %d\", size, len(data)))\n\t}\n\n\treturn nil\n}\n\nfunc exportPrivateKey(tw *tar.Writer, marshalledPrivateKey []byte, filename string) error {\n\tif err := tw.WriteHeader(&tar.Header{\n\t\tTypeflag: tar.TypeReg,\n\t\tName:     filename,\n\t\tMode:     0o600,\n\t\tSize:     int64(len(marshalledPrivateKey)),\n\t}); err != nil {\n\t\treturn errcode.ErrCode_ErrStreamWrite.Wrap(err)\n\t}\n\n\tsize, err := tw.Write(marshalledPrivateKey)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrStreamWrite.Wrap(err)\n\t}\n\n\tif size != len(marshalledPrivateKey) {\n\t\treturn errcode.ErrCode_ErrStreamWrite.Wrap(fmt.Errorf(\"wrote %d bytes instead of %d\", size, len(marshalledPrivateKey)))\n\t}\n\n\treturn nil\n}\n\nfunc (s *service) exportOrbitDBEntry(ctx context.Context, tw *tar.Writer, idStr string) error {\n\tid, err := cid.Parse(idStr)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\tdagNode, err := s.ipfsCoreAPI.Dag().Get(ctx, id)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\n\tdagNodeBytes := dagNode.RawData()\n\n\tif err := tw.WriteHeader(&tar.Header{\n\t\tTypeflag: tar.TypeReg,\n\t\tName:     fmt.Sprintf(\"%s%s\", exportOrbitDBEntriesPrefix, idStr),\n\t\tMode:     0o600,\n\t\tSize:     int64(len(dagNodeBytes)),\n\t}); err != nil {\n\t\treturn errcode.ErrCode_ErrStreamWrite.Wrap(err)\n\t}\n\n\tsize, err := tw.Write(dagNodeBytes)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrStreamWrite.Wrap(err)\n\t}\n\n\tif size != len(dagNodeBytes) {\n\t\treturn errcode.ErrCode_ErrStreamWrite.Wrap(fmt.Errorf(\"wrote %d bytes instead of %d\", size, len(dagNodeBytes)))\n\t}\n\n\treturn nil\n}\n\nfunc readExportSecretKeyFile(expectedSize int64, reader *tar.Reader) ([]byte, error) {\n\tif expectedSize == 0 {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"invalid expected key size\"))\n\t}\n\n\tkeyContents := new(bytes.Buffer)\n\tsize, err := io.Copy(keyContents, reader)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInternal.Wrap(fmt.Errorf(\"unable to read %d bytes: %w\", expectedSize, err))\n\t}\n\n\tif size != expectedSize {\n\t\treturn nil, errcode.ErrCode_ErrInternal.Wrap(fmt.Errorf(\"unexpected file size\"))\n\t}\n\n\treturn keyContents.Bytes(), nil\n}\n\nfunc readExportOrbitDBGroupHeads(expectedSize int64, reader *tar.Reader) (*protocoltypes.GroupHeadsExport, []cid.Cid, []cid.Cid, error) {\n\tif expectedSize == 0 {\n\t\treturn nil, nil, nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"invalid expected node size\"))\n\t}\n\n\tnodeContents := new(bytes.Buffer)\n\tsize, err := io.Copy(nodeContents, reader)\n\tif err != nil {\n\t\treturn nil, nil, nil, errcode.ErrCode_ErrInternal.Wrap(fmt.Errorf(\"unable to read %d bytes: %w\", expectedSize, err))\n\t}\n\n\tif size != expectedSize {\n\t\treturn nil, nil, nil, errcode.ErrCode_ErrInternal.Wrap(fmt.Errorf(\"unexpected file size\"))\n\t}\n\n\tgroupHeads := &protocoltypes.GroupHeadsExport{}\n\tif err := proto.Unmarshal(nodeContents.Bytes(), groupHeads); err != nil {\n\t\treturn nil, nil, nil, errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\tmessagesCIDs := make([]cid.Cid, len(groupHeads.MessagesHeadsCids))\n\tfor i, cidBytes := range groupHeads.MessagesHeadsCids {\n\t\tmessagesCIDs[i], err = cid.Parse(cidBytes)\n\t\tif err != nil {\n\t\t\treturn nil, nil, nil, errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t\t}\n\t}\n\n\tmetaCIDs := make([]cid.Cid, len(groupHeads.MetadataHeadsCids))\n\tfor i, cidBytes := range groupHeads.MetadataHeadsCids {\n\t\tmetaCIDs[i], err = cid.Parse(cidBytes)\n\t\tif err != nil {\n\t\t\treturn nil, nil, nil, errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t\t}\n\t}\n\n\treturn groupHeads, metaCIDs, messagesCIDs, nil\n}\n\nfunc readExportCBORNode(expectedSize int64, cidStr string, reader *tar.Reader) (*cbornode.Node, error) {\n\tif expectedSize == 0 {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"invalid expected node size\"))\n\t}\n\n\tnodeContents := new(bytes.Buffer)\n\texpectedCID, err := cid.Parse(cidStr)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrDeserialization.Wrap(fmt.Errorf(\"unable to parse CID in filename\"))\n\t}\n\n\tsize, err := io.Copy(nodeContents, reader)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInternal.Wrap(fmt.Errorf(\"unable to read %d bytes: %w\", expectedSize, err))\n\t}\n\n\tif size != expectedSize {\n\t\treturn nil, errcode.ErrCode_ErrInternal.Wrap(fmt.Errorf(\"unexpected file size\"))\n\t}\n\n\tnode, err := cbornode.Decode(nodeContents.Bytes(), mh.SHA2_256, -1)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\tif !node.Cid().Equals(expectedCID) {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"entry CID doesn't match file CID\"))\n\t}\n\n\treturn node, nil\n}\n\ntype RestoreAccountHandler struct {\n\tHandler     func(header *tar.Header, reader *tar.Reader) (bool, error)\n\tPostProcess func() error\n}\n\ntype restoreAccountState struct {\n\tkeys map[string][]byte\n}\n\nfunc (state *restoreAccountState) readKey(keyName string) RestoreAccountHandler {\n\treturn RestoreAccountHandler{\n\t\tHandler: func(header *tar.Header, reader *tar.Reader) (bool, error) {\n\t\t\tif header.Name != keyName {\n\t\t\t\treturn false, nil\n\t\t\t}\n\n\t\t\tif state.keys[keyName] != nil {\n\t\t\t\treturn false, errcode.ErrCode_ErrInternal.Wrap(fmt.Errorf(\"multiple keys found in archive\"))\n\t\t\t}\n\n\t\t\tvar err error\n\n\t\t\tstate.keys[keyName], err = readExportSecretKeyFile(header.Size, reader)\n\t\t\tif err != nil {\n\t\t\t\treturn true, errcode.ErrCode_ErrInternal.Wrap(err)\n\t\t\t}\n\n\t\t\treturn true, nil\n\t\t},\n\t}\n}\n\nfunc (state *restoreAccountState) restoreKeys(odb *WeshOrbitDB) RestoreAccountHandler {\n\treturn RestoreAccountHandler{\n\t\tPostProcess: func() error {\n\t\t\tif err := odb.secretStore.ImportAccountKeys(state.keys[exportAccountKeyFilename], state.keys[exportAccountProofKeyFilename]); err != nil {\n\t\t\t\treturn errcode.ErrCode_ErrInternal.Wrap(err)\n\t\t\t}\n\n\t\t\treturn nil\n\t\t},\n\t}\n}\n\nfunc restoreOrbitDBEntry(ctx context.Context, coreAPI coreiface.CoreAPI) RestoreAccountHandler {\n\treturn RestoreAccountHandler{\n\t\tHandler: func(header *tar.Header, reader *tar.Reader) (bool, error) {\n\t\t\tif !strings.HasPrefix(header.Name, exportOrbitDBEntriesPrefix) {\n\t\t\t\treturn false, nil\n\t\t\t}\n\n\t\t\tcidStr := strings.TrimPrefix(header.Name, exportOrbitDBEntriesPrefix)\n\n\t\t\tnode, err := readExportCBORNode(header.Size, cidStr, reader)\n\t\t\tif err != nil {\n\t\t\t\treturn true, errcode.ErrCode_ErrInternal.Wrap(err)\n\t\t\t}\n\n\t\t\tif err := coreAPI.Dag().Add(ctx, node); err != nil {\n\t\t\t\treturn true, errcode.ErrCode_ErrInternal.Wrap(err)\n\t\t\t}\n\n\t\t\treturn true, nil\n\t\t},\n\t}\n}\n\nfunc restoreOrbitDBHeads(ctx context.Context, odb *WeshOrbitDB) RestoreAccountHandler {\n\treturn RestoreAccountHandler{\n\t\tHandler: func(header *tar.Header, reader *tar.Reader) (bool, error) {\n\t\t\tif !strings.HasPrefix(header.Name, exportOrbitDBHeadsPrefix) {\n\t\t\t\treturn false, nil\n\t\t\t}\n\n\t\t\theads, metaCIDs, messageCIDs, err := readExportOrbitDBGroupHeads(header.Size, reader)\n\t\t\tif err != nil {\n\t\t\t\treturn true, errcode.ErrCode_ErrInternal.Wrap(err)\n\t\t\t}\n\n\t\t\tif err := odb.setHeadsForGroup(ctx, &protocoltypes.Group{\n\t\t\t\tPublicKey: heads.PublicKey,\n\t\t\t\tSignPub:   heads.SignPub,\n\t\t\t\tLinkKey:   heads.LinkKey,\n\t\t\t}, metaCIDs, messageCIDs); err != nil {\n\t\t\t\treturn true, errcode.ErrCode_ErrOrbitDBAppend.Wrap(fmt.Errorf(\"error while restoring db head: %w\", err))\n\t\t\t}\n\n\t\t\treturn true, nil\n\t\t},\n\t}\n}\n\nfunc RestoreAccountExport(ctx context.Context, reader io.Reader, coreAPI coreiface.CoreAPI, odb *WeshOrbitDB, logger *zap.Logger, handlers ...RestoreAccountHandler) error {\n\ttr := tar.NewReader(reader)\n\tstate := restoreAccountState{\n\t\tkeys: map[string][]byte{},\n\t}\n\n\thandlers = append(\n\t\t[]RestoreAccountHandler{\n\t\t\tstate.readKey(exportAccountKeyFilename),\n\t\t\tstate.readKey(exportAccountProofKeyFilename),\n\t\t\tstate.restoreKeys(odb),\n\t\t\trestoreOrbitDBEntry(ctx, coreAPI),\n\t\t\trestoreOrbitDBHeads(ctx, odb),\n\t\t},\n\t\thandlers...,\n\t)\n\n\tfor {\n\t\theader, err := tr.Next()\n\n\t\tif err == io.EOF {\n\t\t\tbreak\n\t\t} else if err != nil {\n\t\t\treturn errcode.ErrCode_ErrInternal.Wrap(err)\n\t\t}\n\n\t\tif header.Typeflag != tar.TypeReg {\n\t\t\tlogger.Warn(\"invalid entry type\", zap.String(\"filename\", header.Name), zap.Any(\"filename\", header.Typeflag))\n\t\t\tcontinue\n\t\t}\n\n\t\tnotHandled := true\n\n\t\tfor _, h := range handlers {\n\t\t\tif h.Handler == nil {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\thandled, err := h.Handler(header, tr)\n\t\t\tif err != nil {\n\t\t\t\treturn errcode.ErrCode_ErrInternal.Wrap(err)\n\t\t\t}\n\n\t\t\tif handled {\n\t\t\t\tnotHandled = false\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tif notHandled {\n\t\t\tlogger.Warn(\"unknown export entry\", zap.String(\"filename\", header.Name))\n\t\t}\n\t}\n\n\tfor _, h := range handlers {\n\t\tif h.PostProcess == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tif err := h.PostProcess(); err != nil {\n\t\t\treturn errcode.ErrCode_ErrInternal.Wrap(err)\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "account_export_test.go",
    "content": "package weshnet\n\nimport (\n\t\"archive/tar\"\n\t\"context\"\n\t\"io\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/ipfs/go-cid\"\n\tds \"github.com/ipfs/go-datastore\"\n\tdsync \"github.com/ipfs/go-datastore/sync\"\n\tmocknet \"github.com/libp2p/go-libp2p/p2p/net/mock\"\n\t\"github.com/stretchr/testify/require\"\n\n\torbitdb \"berty.tech/go-orbit-db\"\n\t\"berty.tech/go-orbit-db/pubsub/pubsubraw\"\n\t\"berty.tech/weshnet/v2/pkg/ipfsutil\"\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n\t\"berty.tech/weshnet/v2/pkg/secretstore\"\n\t\"berty.tech/weshnet/v2/pkg/testutil\"\n\t\"berty.tech/weshnet/v2/pkg/tinder\"\n)\n\nfunc Test_service_exportAccountKeys(t *testing.T) {\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\tmn := mocknet.New()\n\tdefer mn.Close()\n\n\tmsrv := tinder.NewMockDriverServer()\n\n\tdsA := dsync.MutexWrap(ds.NewMapDatastore())\n\tnodeA, closeNodeA := NewTestingProtocol(ctx, t, &TestingOpts{\n\t\tMocknet:         mn,\n\t\tDiscoveryServer: msrv,\n\t}, dsA)\n\tdefer closeNodeA()\n\n\t// time.Sleep(time.Second * 5)\n\n\ts, ok := nodeA.Service.(*service)\n\trequire.True(t, ok)\n\n\ttmpFile, err := os.CreateTemp(os.TempDir(), \"test-export-\")\n\trequire.NoError(t, err)\n\n\tdefer os.Remove(tmpFile.Name())\n\n\ttw := tar.NewWriter(tmpFile)\n\n\terr = s.exportAccountKeys(tw)\n\trequire.NoError(t, err)\n\n\terr = tw.Close()\n\trequire.NoError(t, err)\n\n\t_, err = tmpFile.Seek(0, io.SeekStart)\n\trequire.NoError(t, err)\n\n\ttr := tar.NewReader(tmpFile)\n\n\taccountPrivateKey := getKeyFromTar(t, tr, exportAccountKeyFilename)\n\taccountProofPrivateKey := getKeyFromTar(t, tr, exportAccountProofKeyFilename)\n\n\tinStoreAccountPrivateKeyBytes, inStoreAccountProofPrivateKeyBytes, err := s.secretStore.ExportAccountKeysForBackup()\n\trequire.NoError(t, err)\n\trequire.NotNil(t, inStoreAccountPrivateKeyBytes)\n\trequire.NotNil(t, inStoreAccountProofPrivateKeyBytes)\n\n\trequire.Equal(t, accountPrivateKey, inStoreAccountPrivateKeyBytes)\n\trequire.Equal(t, accountProofPrivateKey, inStoreAccountProofPrivateKeyBytes)\n}\n\nfunc getKeyFromTar(t *testing.T, tr *tar.Reader, expectedFilename string) []byte {\n\theader, err := tr.Next()\n\trequire.NoError(t, err)\n\trequire.Equal(t, expectedFilename, header.Name)\n\n\tkeyContents := make([]byte, header.Size)\n\n\tsize, err := tr.Read(keyContents)\n\trequire.Equal(t, int(header.Size), size)\n\n\treturn keyContents\n}\n\nfunc TestFlappyRestoreAccount(t *testing.T) {\n\ttestutil.FilterStability(t, testutil.Flappy)\n\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\tlogger, cleanup := testutil.Logger(t)\n\tdefer cleanup()\n\n\tmn := mocknet.New()\n\tdefer mn.Close()\n\n\tmsrv := tinder.NewMockDriverServer()\n\n\ttmpFile, err := os.CreateTemp(os.TempDir(), \"test-export-\")\n\trequire.NoError(t, err)\n\n\texpectedMessages := map[cid.Cid][]byte{}\n\tvar nodeAInstanceConfig *protocoltypes.ServiceGetConfiguration_Reply\n\n\tg, _, err := NewGroupMultiMember()\n\trequire.NoError(t, err)\n\n\tdefer os.Remove(tmpFile.Name())\n\n\t{\n\t\tdsA := dsync.MutexWrap(ds.NewMapDatastore())\n\t\tnodeA, closeNodeA := NewTestingProtocol(ctx, t, &TestingOpts{\n\t\t\tMocknet: mn,\n\t\t}, dsA)\n\n\t\tserviceA, ok := nodeA.Service.(*service)\n\t\trequire.True(t, ok)\n\n\t\tnodeAInstanceConfig, err = nodeA.Client.ServiceGetConfiguration(ctx, &protocoltypes.ServiceGetConfiguration_Request{})\n\t\trequire.NoError(t, err)\n\t\trequire.NotNil(t, nodeAInstanceConfig)\n\n\t\ttestPayload1 := []byte(\"testMessage1\")\n\t\ttestPayload2 := []byte(\"testMessage2\")\n\t\ttestPayload3 := []byte(\"testMessage3\")\n\t\ttestPayload4 := []byte(\"testMessage4\")\n\n\t\taccountGroup := serviceA.getAccountGroup()\n\t\trequire.NotNil(t, accountGroup)\n\n\t\top, err := accountGroup.messageStore.AddMessage(ctx, testPayload1)\n\t\trequire.NoError(t, err)\n\n\t\texpectedMessages[op.GetEntry().GetHash()] = testPayload1\n\n\t\top, err = accountGroup.messageStore.AddMessage(ctx, testPayload2)\n\t\trequire.NoError(t, err)\n\n\t\texpectedMessages[op.GetEntry().GetHash()] = testPayload2\n\n\t\t_, err = nodeA.Client.MultiMemberGroupJoin(ctx, &protocoltypes.MultiMemberGroupJoin_Request{Group: g})\n\t\trequire.NoError(t, err)\n\n\t\t_, err = nodeA.Client.ActivateGroup(ctx, &protocoltypes.ActivateGroup_Request{GroupPk: g.PublicKey})\n\t\trequire.NoError(t, err)\n\n\t\top, err = serviceA.openedGroups[string(g.PublicKey)].messageStore.AddMessage(ctx, testPayload3)\n\t\trequire.NoError(t, err)\n\n\t\texpectedMessages[op.GetEntry().GetHash()] = testPayload3\n\n\t\top, err = serviceA.openedGroups[string(g.PublicKey)].messageStore.AddMessage(ctx, testPayload4)\n\t\trequire.NoError(t, err)\n\n\t\texpectedMessages[op.GetEntry().GetHash()] = testPayload4\n\n\t\trequire.NoError(t, serviceA.export(ctx, tmpFile))\n\n\t\tcloseNodeA()\n\t\trequire.NoError(t, dsA.Close())\n\t}\n\n\t_, err = tmpFile.Seek(0, io.SeekStart)\n\trequire.NoError(t, err)\n\n\t{\n\t\tdsB := dsync.MutexWrap(ds.NewMapDatastore())\n\t\tsecretStoreB, err := secretstore.NewSecretStore(dsB, nil)\n\t\trequire.NoError(t, err)\n\n\t\tipfsNodeB := ipfsutil.TestingCoreAPIUsingMockNet(ctx, t, &ipfsutil.TestingAPIOpts{\n\t\t\tMocknet:   mn,\n\t\t\tDatastore: dsB,\n\t\t})\n\n\t\todb, err := NewWeshOrbitDB(ctx, ipfsNodeB.API(), &NewOrbitDBOptions{\n\t\t\tNewOrbitDBOptions: orbitdb.NewOrbitDBOptions{\n\t\t\t\tPubSub: pubsubraw.NewPubSub(ipfsNodeB.PubSub(), ipfsNodeB.MockNode().PeerHost.ID(), logger, nil),\n\t\t\t\tLogger: logger,\n\t\t\t},\n\t\t\tDatastore:   dsB,\n\t\t\tSecretStore: secretStoreB,\n\t\t})\n\t\trequire.NoError(t, err)\n\n\t\terr = RestoreAccountExport(ctx, tmpFile, ipfsNodeB.API(), odb, logger)\n\t\trequire.NoError(t, err)\n\n\t\tnodeB, closeNodeB := NewTestingProtocol(ctx, t, &TestingOpts{\n\t\t\tMocknet:         mn,\n\t\t\tDiscoveryServer: msrv,\n\t\t\tSecretStore:     secretStoreB,\n\t\t\tCoreAPIMock:     ipfsNodeB,\n\t\t\tOrbitDB:         odb,\n\t\t}, dsB)\n\t\tdefer closeNodeB()\n\n\t\tnodeBInstanceConfig, err := nodeB.Client.ServiceGetConfiguration(ctx, &protocoltypes.ServiceGetConfiguration_Request{})\n\t\trequire.NoError(t, err)\n\n\t\trequire.NotNil(t, nodeBInstanceConfig)\n\t\trequire.Equal(t, nodeAInstanceConfig.AccountPk, nodeBInstanceConfig.AccountPk)\n\t\trequire.NotEqual(t, nodeAInstanceConfig.DevicePk, nodeBInstanceConfig.DevicePk)\n\t\trequire.Equal(t, nodeAInstanceConfig.AccountGroupPk, nodeBInstanceConfig.AccountGroupPk)\n\n\t\taccountGroup := nodeB.Service.(*service).getAccountGroup()\n\t\trequire.NotNil(t, accountGroup)\n\n\t\tentries := accountGroup.messageStore.OpLog().GetEntries()\n\t\tfor _, evt := range entries.Slice() {\n\t\t\t_, ok := expectedMessages[evt.GetHash()]\n\t\t\trequire.True(t, ok)\n\t\t}\n\n\t\t_, err = nodeB.Service.ActivateGroup(ctx, &protocoltypes.ActivateGroup_Request{GroupPk: g.PublicKey})\n\t\trequire.NoError(t, err)\n\n\t\tfor _, gPK := range [][]byte{nodeBInstanceConfig.AccountGroupPk, g.PublicKey} {\n\t\t\tsub, err := nodeB.Client.GroupMessageList(\n\t\t\t\tctx,\n\t\t\t\t&protocoltypes.GroupMessageList_Request{\n\t\t\t\t\tGroupPk:  gPK,\n\t\t\t\t\tUntilNow: true,\n\t\t\t\t},\n\t\t\t)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tfor {\n\t\t\t\tevt, err := sub.Recv()\n\t\t\t\tif err != nil {\n\t\t\t\t\trequire.Equal(t, io.EOF, err)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\n\t\t\t\tid, err := cid.Parse(evt.EventContext.Id)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tref, ok := expectedMessages[id]\n\t\t\t\trequire.True(t, ok)\n\t\t\t\trequire.Equal(t, ref, evt.Message)\n\n\t\t\t\tdelete(expectedMessages, id)\n\t\t\t}\n\t\t}\n\n\t\trequire.Empty(t, expectedMessages)\n\t}\n\t// TODO: test account metadata entries\n}\n"
  },
  {
    "path": "api/go-internal/buf.yaml",
    "content": "version: v2\nname: buf.build/berty-technologies/weshnet\nbreaking:\n    use:\n        - FILE\nlint:\n    use:\n        - DEFAULT\n"
  },
  {
    "path": "api/go-internal/handshake/handshake.proto",
    "content": "syntax = \"proto3\";\n\npackage handshake;\n\noption go_package = \"berty.tech/weshnet/v2/internal/handshake\";\n\nmessage BoxEnvelope {\n  bytes box = 1;\n}\n\nmessage HelloPayload {\n  bytes ephemeral_pub_key = 1;\n}\n\nmessage RequesterAuthenticatePayload {\n  bytes requester_account_id = 1;\n  bytes requester_account_sig = 2;\n}\n\nmessage ResponderAcceptPayload {\n  bytes responder_account_sig = 1;\n}\n\nmessage RequesterAcknowledgePayload {\n  bool success = 1;\n}\n"
  },
  {
    "path": "api/go-internal/tinder/records.proto",
    "content": "syntax = \"proto3\";\n\npackage tinder;\n\noption go_package = \"berty.tech/weshnet/v2/pkg/tinder\";\n\nmessage Records {\n\trepeated Record records = 1;\n}\n\nmessage Record {\n\tstring cid = 1;\n \tint64 expire = 2;\n}\n"
  },
  {
    "path": "api/protocol/buf.yaml",
    "content": "version: v2\nname: buf.build/berty-technologies/weshnet\ndeps:\n    - buf.build/srikrsna/protoc-gen-gotag\nbreaking:\n    use:\n        - FILE\nlint:\n    use:\n        - DEFAULT\n"
  },
  {
    "path": "api/protocol/errcode/errcode.proto",
    "content": "syntax = \"proto3\";\n\npackage weshnet.errcode;\n\noption go_package = \"berty.tech/weshnet/v2/pkg/errcode\";\n\nenum ErrCode {\n  //----------------\n  // Special errors\n  //----------------\n\n  Undefined = 0; // default value, should never be set manually\n\n  TODO = 666;              // indicates that you plan to create an error later\n  ErrNotImplemented = 777; // indicates that a method is not implemented yet\n  ErrInternal = 888; // indicates an unknown error (without Code), i.e. in gRPC\n\n  //----------------\n  // Generic errors\n  //----------------\n\n  // Parameters and I/O errors\n\n  ErrInvalidInput = 100;\n  ErrInvalidRange = 101;\n  ErrMissingInput = 102;\n  ErrSerialization = 103;\n  ErrDeserialization = 104;\n  ErrStreamRead = 105;\n  ErrStreamWrite = 106;\n  ErrStreamTransform = 110;\n  ErrStreamSendAndClose = 111;\n  ErrStreamHeaderWrite = 112;\n  ErrStreamHeaderRead = 115;\n  ErrStreamSink = 113;\n  ErrStreamCloseAndRecv = 114;\n  ErrMissingMapKey = 107;\n  ErrDBWrite = 108;\n  ErrDBRead = 109;\n  ErrDBDestroy = 120;\n  ErrDBMigrate = 121;\n  ErrDBReplay = 122;\n  ErrDBRestore = 123;\n  ErrDBOpen = 124;\n  ErrDBClose = 125;\n\n  // Crypto errors\n\n  ErrCryptoRandomGeneration = 200;\n  ErrCryptoKeyGeneration = 201;\n  ErrCryptoNonceGeneration = 202;\n  ErrCryptoSignature = 203;\n  ErrCryptoSignatureVerification = 204;\n  ErrCryptoDecrypt = 205;\n  ErrCryptoDecryptPayload = 206;\n  ErrCryptoEncrypt = 207;\n  ErrCryptoKeyConversion = 208;\n  ErrCryptoCipherInit = 209;\n  ErrCryptoKeyDerivation = 210;\n\n  // Pattern errors\n\n  ErrMap = 300;\n  ErrForEach = 301;\n\n  // Keystore errors\n\n  ErrKeystoreGet = 400;\n  ErrKeystorePut = 401;\n  ErrNotFound = 404; // generic\n\n  //-----------------\n  // Specific errors\n  //-----------------\n\n  // OrbitDB errors\n\n  ErrOrbitDBInit = 1000;\n  ErrOrbitDBOpen = 1001;\n  ErrOrbitDBAppend = 1002;\n  ErrOrbitDBDeserialization = 1003;\n  ErrOrbitDBStoreCast = 1004;\n\n  // Handshake errors\n\n  ErrHandshakeOwnEphemeralKeyGenSend = 1100;\n  ErrHandshakePeerEphemeralKeyRecv = 1101;\n  ErrHandshakeRequesterAuthenticateBoxKeyGen = 1102;\n  ErrHandshakeResponderAcceptBoxKeyGen = 1103;\n  ErrHandshakeRequesterHello = 1104;\n  ErrHandshakeResponderHello = 1105;\n  ErrHandshakeRequesterAuthenticate = 1106;\n  ErrHandshakeResponderAccept = 1107;\n  ErrHandshakeRequesterAcknowledge = 1108;\n\n  // Contact Request errors\n\n  ErrContactRequestSameAccount = 1200;\n  ErrContactRequestContactAlreadyAdded = 1201;\n  ErrContactRequestContactBlocked = 1202;\n  ErrContactRequestContactUndefined = 1203;\n  ErrContactRequestIncomingAlreadyReceived = 1204;\n\n  // Group errors\n\n  ErrGroupMemberLogEventOpen = 1300;\n  ErrGroupMemberLogEventSignature = 1301;\n  ErrGroupMemberUnknownGroupID = 1302;\n  ErrGroupSecretOtherDestMember = 1303;\n  ErrGroupSecretAlreadySentToMember = 1304;\n  ErrGroupInvalidType = 1305;\n  ErrGroupMissing = 1306;\n  ErrGroupActivate = 1307;\n  ErrGroupDeactivate = 1308;\n  ErrGroupInfo = 1309;\n  ErrGroupUnknown = 1310;\n  ErrGroupOpen = 1311;\n\n  // Message key errors\n\n  ErrMessageKeyPersistencePut = 1500;\n  ErrMessageKeyPersistenceGet = 1501;\n\n  // Services Replication\n\n  ErrServiceReplication = 4100;\n  ErrServiceReplicationServer = 4101;\n  ErrServiceReplicationMissingEndpoint = 4102;\n\n  // Services Directory\n\n  ErrServicesDirectory = 4200;\n  ErrServicesDirectoryInvalidVerifiedCredentialSubject = 4201;\n  ErrServicesDirectoryExistingRecordNotFound = 4202;\n  ErrServicesDirectoryRecordLockedAndCantBeReplaced = 4203;\n  ErrServicesDirectoryExplicitReplaceFlagRequired = 4204;\n  ErrServicesDirectoryInvalidVerifiedCredential = 4205;\n  ErrServicesDirectoryExpiredVerifiedCredential = 4206;\n  ErrServicesDirectoryInvalidVerifiedCredentialID = 4207;\n}\n\nmessage ErrDetails { repeated ErrCode codes = 1; }\n"
  },
  {
    "path": "api/protocol/outofstoremessagetypes/outofstoremessage.proto",
    "content": "syntax = \"proto3\";\n\npackage weshnet.outofstoremessage.v1;\n\nimport \"protocoltypes.proto\";\n\noption go_package = \"berty.tech/weshnet/v2/pkg/outofstoremessagetypes\";\n\n// OutOfStoreMessageService is the service used to open out-of-store messages (e.g. push notifications)\n// It is used to open messages with a lightweight protocol service for mobile backgroup processes.\nservice OutOfStoreMessageService {\n  // OutOfStoreReceive parses a payload received outside a synchronized store\n  rpc OutOfStoreReceive(weshnet.protocol.v1.OutOfStoreReceive.Request) returns (weshnet.protocol.v1.OutOfStoreReceive.Reply);\n}\n"
  },
  {
    "path": "api/protocol/protocoltypes.proto",
    "content": "syntax = \"proto3\";\n\npackage weshnet.protocol.v1;\n\noption go_package = \"berty.tech/weshnet/v2/pkg/protocoltypes\";\n\n// ProtocolService is the top-level API to manage the Wesh protocol service.\n// Each active Wesh protocol service is considered as a Wesh device and is associated with a Wesh user.\nservice ProtocolService {\n  // ServiceExportData exports the current data of the protocol service\n  rpc ServiceExportData (ServiceExportData.Request) returns (stream ServiceExportData.Reply);\n\n  // ServiceGetConfiguration gets the current configuration of the protocol service\n  rpc ServiceGetConfiguration (ServiceGetConfiguration.Request) returns (ServiceGetConfiguration.Reply);\n\n  // ContactRequestReference retrieves the information required to create a reference (ie. included in a shareable link) to the current account\n  rpc ContactRequestReference (ContactRequestReference.Request) returns (ContactRequestReference.Reply);\n\n  // ContactRequestDisable disables incoming contact requests\n  rpc ContactRequestDisable (ContactRequestDisable.Request) returns (ContactRequestDisable.Reply);\n\n  // ContactRequestEnable enables incoming contact requests\n  rpc ContactRequestEnable (ContactRequestEnable.Request) returns (ContactRequestEnable.Reply);\n\n  // ContactRequestResetReference changes the contact request reference\n  rpc ContactRequestResetReference (ContactRequestResetReference.Request) returns (ContactRequestResetReference.Reply);\n\n  // ContactRequestSend attempt to send a contact request\n  rpc ContactRequestSend (ContactRequestSend.Request) returns (ContactRequestSend.Reply);\n\n  // ContactRequestAccept accepts a contact request\n  rpc ContactRequestAccept (ContactRequestAccept.Request) returns (ContactRequestAccept.Reply);\n\n  // ContactRequestDiscard ignores a contact request, without informing the other user\n  rpc ContactRequestDiscard (ContactRequestDiscard.Request) returns (ContactRequestDiscard.Reply);\n\n  // ShareContact uses ContactRequestReference to get the contact information for the current account and\n  // returns the Protobuf encoding of a shareable contact which you can further encode and share. If needed, this\n  // will reset the contact request reference and enable contact requests. To decode the result, see DecodeContact.\n  rpc ShareContact (ShareContact.Request) returns (ShareContact.Reply);\n\n  // DecodeContact decodes the Protobuf encoding of a shareable contact which was returned by ShareContact.\n  rpc DecodeContact (DecodeContact.Request) returns (DecodeContact.Reply);\n\n  // ContactBlock blocks a contact from sending requests\n  rpc ContactBlock (ContactBlock.Request) returns (ContactBlock.Reply);\n\n  // ContactUnblock unblocks a contact from sending requests\n  rpc ContactUnblock (ContactUnblock.Request) returns (ContactUnblock.Reply);\n\n  // ContactAliasKeySend send an alias key to a contact, the contact will be able to assert that your account is being present on a multi-member group\n  rpc ContactAliasKeySend (ContactAliasKeySend.Request) returns (ContactAliasKeySend.Reply);\n\n  // MultiMemberGroupCreate creates a new multi-member group\n  rpc MultiMemberGroupCreate (MultiMemberGroupCreate.Request) returns (MultiMemberGroupCreate.Reply);\n\n  // MultiMemberGroupJoin joins a multi-member group\n  rpc MultiMemberGroupJoin (MultiMemberGroupJoin.Request) returns (MultiMemberGroupJoin.Reply);\n\n  // MultiMemberGroupLeave leaves a multi-member group\n  rpc MultiMemberGroupLeave (MultiMemberGroupLeave.Request) returns (MultiMemberGroupLeave.Reply);\n\n  // MultiMemberGroupAliasResolverDisclose discloses your alias resolver key\n  rpc MultiMemberGroupAliasResolverDisclose (MultiMemberGroupAliasResolverDisclose.Request) returns (MultiMemberGroupAliasResolverDisclose.Reply);\n\n  // MultiMemberGroupAdminRoleGrant grants an admin role to a group member\n  rpc MultiMemberGroupAdminRoleGrant (MultiMemberGroupAdminRoleGrant.Request) returns (MultiMemberGroupAdminRoleGrant.Reply);\n\n  // MultiMemberGroupInvitationCreate creates an invitation to a multi-member group\n  rpc MultiMemberGroupInvitationCreate (MultiMemberGroupInvitationCreate.Request) returns (MultiMemberGroupInvitationCreate.Reply);\n\n  // AppMetadataSend adds an app event to the metadata store, the message is encrypted using a symmetric key and readable by future group members\n  rpc AppMetadataSend (AppMetadataSend.Request) returns (AppMetadataSend.Reply);\n\n  // AppMessageSend adds an app event to the message store, the message is encrypted using a derived key and readable by current group members\n  rpc AppMessageSend (AppMessageSend.Request) returns (AppMessageSend.Reply);\n\n  // GroupMetadataList replays previous and subscribes to new metadata events from the group\n  rpc GroupMetadataList (GroupMetadataList.Request) returns (stream GroupMetadataEvent);\n\n  // GroupMessageList replays previous and subscribes to new message events from the group\n  rpc GroupMessageList (GroupMessageList.Request) returns (stream GroupMessageEvent);\n\n  // GroupInfo retrieves information about a group\n  rpc GroupInfo (GroupInfo.Request) returns (GroupInfo.Reply);\n\n  // ActivateGroup explicitly opens a group\n  rpc ActivateGroup (ActivateGroup.Request) returns (ActivateGroup.Reply);\n\n  // DeactivateGroup closes a group\n  rpc DeactivateGroup (DeactivateGroup.Request) returns (DeactivateGroup.Reply);\n\n  // GroupDeviceStatus monitor device status\n  rpc GroupDeviceStatus(GroupDeviceStatus.Request) returns (stream GroupDeviceStatus.Reply);\n\n  rpc DebugListGroups (DebugListGroups.Request) returns (stream DebugListGroups.Reply);\n\n  rpc DebugInspectGroupStore (DebugInspectGroupStore.Request) returns (stream DebugInspectGroupStore.Reply);\n\n  rpc DebugGroup (DebugGroup.Request) returns (DebugGroup.Reply);\n\n  rpc SystemInfo (SystemInfo.Request) returns (SystemInfo.Reply);\n\n  // CredentialVerificationServiceInitFlow Initialize a credential verification flow\n  rpc CredentialVerificationServiceInitFlow (CredentialVerificationServiceInitFlow.Request) returns (CredentialVerificationServiceInitFlow.Reply);\n\n  // CredentialVerificationServiceCompleteFlow Completes a credential verification flow\n  rpc CredentialVerificationServiceCompleteFlow (CredentialVerificationServiceCompleteFlow.Request) returns (CredentialVerificationServiceCompleteFlow.Reply);\n\n  // VerifiedCredentialsList Retrieves the list of verified credentials\n  rpc VerifiedCredentialsList (VerifiedCredentialsList.Request) returns (stream VerifiedCredentialsList.Reply);\n\n  // ReplicationServiceRegisterGroup Asks a replication service to distribute a group contents\n  rpc ReplicationServiceRegisterGroup (ReplicationServiceRegisterGroup.Request) returns (ReplicationServiceRegisterGroup.Reply);\n\n  // PeerList returns a list of P2P peers\n  rpc PeerList(PeerList.Request) returns (PeerList.Reply);\n\n  // OutOfStoreReceive parses a payload received outside a synchronized store\n  rpc OutOfStoreReceive(OutOfStoreReceive.Request) returns (OutOfStoreReceive.Reply);\n\n  // OutOfStoreSeal creates a payload of a message present in store to be sent outside a synchronized store\n  rpc OutOfStoreSeal(OutOfStoreSeal.Request) returns (OutOfStoreSeal.Reply);\n\n  // RefreshContactRequest try to refresh the contact request for the given contact\n  rpc RefreshContactRequest(RefreshContactRequest.Request) returns (RefreshContactRequest.Reply);\n}\n\n\nenum GroupType {\n  // GroupTypeUndefined indicates that the value has not been set. For example, happens if group is replicated.\n  GroupTypeUndefined = 0;\n\n  // GroupTypeAccount is the group managing an account, available to all its devices.\n  GroupTypeAccount = 1;\n\n  // GroupTypeContact is the group created between two accounts, available to all their devices.\n  GroupTypeContact = 2;\n\n  // GroupTypeMultiMember is a group containing an undefined number of members.\n  GroupTypeMultiMember = 3;\n\n  // Following group types have not been defined, first is a group with\n  // only approved writers, second is public group with anyone allowed to\n  // write, in both cases full history is available to new members.\n  //\n  // GroupTypeChannel = 4;\n  // GroupTypePublic = 5;\n}\n\nenum EventType {\n  // EventTypeUndefined indicates that the value has not been set. Should not happen.\n  EventTypeUndefined = 0;\n\n  // EventTypeGroupMemberDeviceAdded indicates the payload includes that a member has added their device to the group\n  EventTypeGroupMemberDeviceAdded = 1;\n\n  // EventTypeGroupDeviceChainKeyAdded indicates the payload includes that a member has sent their device chain key to another member\n  EventTypeGroupDeviceChainKeyAdded = 2;\n\n  // EventTypeGroupAdditionalRendezvousSeedAdded adds a new rendezvous seed to a group\n  // Might be implemented later, could be useful for replication services\n  // EventTypeGroupAdditionalRendezvousSeedAdded = 3;\n\n  // EventTypeGroupAdditionalRendezvousSeedRemoved removes a rendezvous seed from a group\n  // Might be implemented later, could be useful for replication services\n  // EventTypeGroupAdditionalRendezvousSeedRemoved = 4;\n\n  // EventTypeAccountGroupJoined indicates the payload includes that the account has joined a group\n  EventTypeAccountGroupJoined = 101;\n\n  // EventTypeAccountGroupLeft indicates the payload includes that the account has left a group\n  EventTypeAccountGroupLeft = 102;\n\n  // EventTypeAccountContactRequestDisabled indicates the payload includes that the account has disabled incoming contact requests\n  EventTypeAccountContactRequestDisabled = 103;\n\n  // EventTypeAccountContactRequestEnabled indicates the payload includes that the account has enabled incoming contact requests\n  EventTypeAccountContactRequestEnabled = 104;\n\n  // EventTypeAccountContactRequestReferenceReset indicates the payload includes that the account has a new contact request rendezvous seed\n  EventTypeAccountContactRequestReferenceReset = 105;\n\n  // EventTypeAccountContactRequestOutgoingEnqueued indicates the payload includes that the account will attempt to send a new contact request\n  EventTypeAccountContactRequestOutgoingEnqueued = 106;\n\n  // EventTypeAccountContactRequestOutgoingSent indicates the payload includes that the account has sent a contact request\n  EventTypeAccountContactRequestOutgoingSent = 107;\n\n  // EventTypeAccountContactRequestIncomingReceived indicates the payload includes that the account has received a contact request\n  EventTypeAccountContactRequestIncomingReceived = 108;\n\n  // EventTypeAccountContactRequestIncomingDiscarded indicates the payload includes that the account has ignored a contact request\n  EventTypeAccountContactRequestIncomingDiscarded = 109;\n\n  // EventTypeAccountContactRequestIncomingAccepted indicates the payload includes that the account has accepted a contact request\n  EventTypeAccountContactRequestIncomingAccepted = 110;\n\n  // EventTypeAccountContactBlocked indicates the payload includes that the account has blocked a contact\n  EventTypeAccountContactBlocked = 111;\n\n  // EventTypeAccountContactUnblocked indicates the payload includes that the account has unblocked a contact\n  EventTypeAccountContactUnblocked = 112;\n\n  // EventTypeContactAliasKeyAdded indicates the payload includes that the contact group has received an alias key\n  EventTypeContactAliasKeyAdded = 201;\n\n  // EventTypeMultiMemberGroupAliasResolverAdded indicates the payload includes that a member of the group sent their alias proof\n  EventTypeMultiMemberGroupAliasResolverAdded = 301;\n\n  // EventTypeMultiMemberGroupInitialMemberAnnounced indicates the payload includes that a member has authenticated themselves as the group owner\n  EventTypeMultiMemberGroupInitialMemberAnnounced = 302;\n\n  // EventTypeMultiMemberGroupAdminRoleGranted indicates the payload includes that an admin of the group granted another member as an admin\n  EventTypeMultiMemberGroupAdminRoleGranted = 303;\n\n  // EventTypeGroupReplicating indicates that the group has been registered for replication on a server\n  EventTypeGroupReplicating = 403;\n\n  // EventTypeAccountVerifiedCredentialRegistered\n  EventTypeAccountVerifiedCredentialRegistered = 500;\n\n  // EventTypeGroupMetadataPayloadSent indicates the payload includes an app specific event, unlike messages stored on the message store it is encrypted using a static key\n  EventTypeGroupMetadataPayloadSent = 1001;\n}\n\n// Account describes all the secrets that identifies an Account\nmessage Account {\n  // group specifies which group is used to manage the account\n  Group group = 1;\n\n  // account_private_key, private part is used to signs handshake, signs device, create contacts group keys via ECDH -- public part is used to have a shareable identity\n  bytes account_private_key = 2;\n\n  // alias_private_key, private part is use to derive group members private keys, signs alias proofs, public part can be shared to contacts to prove identity\n  bytes alias_private_key = 3;\n\n  // public_rendezvous_seed, rendezvous seed used for direct communication\n  bytes public_rendezvous_seed = 4;\n}\n\n// Group define a group and is enough to invite someone to it\nmessage Group {\n  // public_key is the identifier of the group, it signs the group secret and the initial member of a multi-member group\n  bytes public_key = 1;\n\n  // secret is the symmetric secret of the group, which is used to encrypt the metadata\n  bytes secret = 2;\n\n  // secret_sig is the signature of the secret used to ensure the validity of the group\n  bytes secret_sig = 3;\n\n  // group_type specifies the type of the group, used to determine how device chain key is generated\n  GroupType group_type = 4;\n\n  // sign_pub is the signature public key used to verify entries, not required when secret and secret_sig are provided\n  bytes sign_pub = 5;\n\n  // link_key is the secret key used to exchange group updates and links to attachments, useful for replication services\n  bytes link_key = 6;\n\n  // link_key_sig is the signature of the link_key using the group private key\n  bytes link_key_sig = 7;\n}\n\nmessage GroupHeadsExport {\n  // public_key is the identifier of the group, it signs the group secret and the initial member of a multi-member group\n  bytes public_key = 1;\n\n  // sign_pub is the signature public key used to verify entries\n  bytes sign_pub = 2;\n\n  // metadata_heads_cids are the heads of the metadata store that should be restored from an export\n  repeated bytes metadata_heads_cids = 3;\n\n  // messages_heads_cids are the heads of the metadata store that should be restored from an export\n  repeated bytes messages_heads_cids = 4;\n\n  // link_key\n  bytes link_key = 5;\n}\n\n// GroupMetadata is used in GroupEnvelope and only readable by invited group members\nmessage GroupMetadata {\n  // event_type defines which event type is used\n  EventType event_type = 1;\n\n  // the serialization depends on event_type, event is symmetrically encrypted\n  bytes payload = 2;\n\n  // sig is the signature of the payload, it depends on the event_type for the used key\n  bytes sig = 3;\n\n  // protocol_metadata is protocol layer data\n  ProtocolMetadata protocol_metadata = 4;\n}\n\n// GroupEnvelope is a publicly exposed structure containing a group metadata event\nmessage GroupEnvelope {\n  // nonce is used to encrypt the message\n  bytes nonce = 1;\n\n  // event is encrypted using a symmetric key shared among group members\n  bytes event = 2;\n\n  reserved 3; // repeated bytes encrypted_attachment_cids = 3 ;\n}\n\n// MessageHeaders is used in MessageEnvelope and only readable by invited group members\nmessage MessageHeaders {\n  // counter is the current counter value for the specified device\n  uint64 counter = 1;\n\n  // device_pk is the public key of the device sending the message\n  bytes device_pk = 2;\n\n  // sig is the signature of the encrypted message using the device's private key\n  bytes sig = 3;\n\n  // metadata allow to pass custom informations\n  map<string, string> metadata = 4;\n}\n\nmessage ProtocolMetadata {\n  // attachments_secrets is a list of secret keys used retrieve attachments\n  reserved 1; //repeated bytes attachments_secrets = 1;\n}\n\n// EncryptedMessage is used in MessageEnvelope and only readable by groups members that joined before the message was sent\nmessage EncryptedMessage {\n  // plaintext is the app layer data\n  bytes plaintext = 1;\n\n  // protocol_metadata is protocol layer data\n  ProtocolMetadata protocol_metadata = 2;\n}\n\n// MessageEnvelope is a publicly exposed structure containing a group secure message\nmessage MessageEnvelope {\n  // message_headers is an encrypted serialization using a symmetric key of a MessageHeaders message\n  bytes message_headers = 1;\n\n  // message is an encrypted message, only readable by group members who previously received the appropriate chain key\n  bytes message = 2;\n\n  // nonce is a nonce for message headers\n  bytes nonce = 3;\n\n  // encrypted_attachment_cids is a list of attachment CIDs encrypted specifically for replication services\n  reserved 4; // repeated bytes encrypted_attachment_cids = 4;\n}\n\n// ***************************************************************************\n// Group event types\n// ***************************************************************************\n\n// EventContext adds context (its id, its parents and its attachments) to an event\nmessage EventContext {\n  // id is the CID of the underlying OrbitDB event\n  bytes id = 1;\n\n  // id are the the CIDs of the underlying parents of the OrbitDB event\n  repeated bytes parent_ids = 2;\n\n  // group_pk receiving the event\n  bytes group_pk = 3;\n\n  // attachment_cids is a list of attachment that can be retrieved\n  reserved 4; // repeated bytes attachment_cids = 4;\n}\n\n// GroupMetadataPayloadSent is an app defined message, accessible to future group members\nmessage GroupMetadataPayloadSent {\n  // device_pk is the device sending the event, signs the message\n  bytes device_pk = 1;\n\n  // message is the payload\n  bytes message = 2;\n}\n\n// ContactAliasKeyAdded is an event type where ones shares their alias public key\nmessage ContactAliasKeyAdded {\n  // device_pk is the device sending the event, signs the message\n  bytes device_pk = 1;\n\n  // alias_pk is the alias key which will be used to verify a contact identity\n  bytes alias_pk = 2;\n}\n\n// GroupMemberDeviceAdded is an event which indicates to a group a new device (and eventually a new member) is joining it\n// When added on AccountGroup, this event should be followed by appropriate GroupMemberDeviceAdded and GroupDeviceChainKeyAdded events\nmessage GroupMemberDeviceAdded {\n  // member_pk is the member sending the event\n  bytes member_pk = 1;\n\n  // device_pk is the device sending the event, signs the message\n  bytes device_pk = 2;\n\n  // member_sig is used to prove the ownership of the member pk\n  bytes member_sig = 3; // TODO: signature of what ??? ensure it can't be replayed\n}\n\n// DeviceChainKey is a chain key, which will be encrypted for a specific member of the group\nmessage DeviceChainKey {\n  // chain_key is the current value of the chain key of the group device\n  bytes chain_key = 1;\n\n  // counter is the current value of the counter of the group device\n  uint64 counter = 2;\n}\n\n// GroupDeviceChainKeyAdded is an event which indicates to a group member a device chain key\nmessage GroupDeviceChainKeyAdded {\n  // device_pk is the device sending the event, signs the message\n  bytes device_pk = 1;\n\n  // dest_member_pk is the member who should receive the secret\n  bytes dest_member_pk = 2;\n\n  // payload is the serialization of Payload encrypted for the specified member\n  bytes payload = 3;\n}\n\n// MultiMemberGroupAliasResolverAdded indicates that a group member want to disclose their presence in the group to their contacts\nmessage MultiMemberGroupAliasResolverAdded {\n  // device_pk is the device sending the event, signs the message\n  bytes device_pk = 1;\n\n  // alias_resolver allows contact of an account to resolve the real identity behind an alias (Multi-Member Group Member)\n  // Generated by both contacts and account independently using: hmac(aliasPK, GroupID)\n  bytes alias_resolver = 2;\n\n  // alias_proof ensures that the associated alias_resolver has been issued by the right account\n  // Generated using aliasSKSig(GroupID)\n  bytes alias_proof = 3;\n}\n\n// MultiMemberGroupAdminRoleGranted indicates that a group admin allows another group member to act as an admin\nmessage MultiMemberGroupAdminRoleGranted {\n  // device_pk is the device sending the event, signs the message, must be the device of an admin of the group\n  bytes device_pk = 1;\n\n  // grantee_member_pk is the member public key of the member granted of the admin role\n  bytes grantee_member_pk = 2;\n}\n\n// MultiMemberGroupInitialMemberAnnounced indicates that a member is the group creator, this event is signed using the group ID private key\nmessage MultiMemberGroupInitialMemberAnnounced {\n  // member_pk is the public key of the member who is the group creator\n  bytes member_pk = 1;\n}\n\n// GroupAddAdditionalRendezvousSeed indicates that an additional rendezvous point should be used for data synchronization\nmessage GroupAddAdditionalRendezvousSeed {\n  // device_pk is the device sending the event, signs the message, must be the device of an admin of the group\n  bytes device_pk = 1;\n\n  // seed is the additional rendezvous point seed which should be used\n  bytes seed = 2;\n}\n\n// GroupRemoveAdditionalRendezvousSeed indicates that a previously added rendezvous point should be removed\nmessage GroupRemoveAdditionalRendezvousSeed {\n  // device_pk is the device sending the event, signs the message, must be the device of an admin of the group\n  bytes device_pk = 1;\n\n  // seed is the additional rendezvous point seed which should be removed\n  bytes seed = 2;\n}\n\n// AccountGroupJoined indicates that the account is now part of a new group\nmessage AccountGroupJoined {\n  // device_pk is the device sending the event, signs the message\n  bytes device_pk = 1;\n\n  // group describe the joined group\n  Group group = 2;\n}\n\n// AccountGroupLeft indicates that the account has left a group\nmessage AccountGroupLeft {\n  // device_pk is the device sending the event, signs the message\n  bytes device_pk = 1;\n\n  // group_pk references the group left\n  bytes group_pk = 2;\n}\n\n// AccountContactRequestDisabled indicates that the account should not be advertised on a public rendezvous point\nmessage AccountContactRequestDisabled {\n  // device_pk is the device sending the event, signs the message\n  bytes device_pk = 1;\n}\n\n// AccountContactRequestEnabled indicates that the account should be advertised on a public rendezvous point\nmessage AccountContactRequestEnabled {\n  // device_pk is the device sending the event, signs the message\n  bytes device_pk = 1;\n}\n\n// AccountContactRequestReferenceReset indicates that the account should be advertised on different public rendezvous points\nmessage AccountContactRequestReferenceReset {\n  // device_pk is the device sending the event, signs the message\n  bytes device_pk = 1;\n\n  // public_rendezvous_seed is the new rendezvous point seed\n  bytes public_rendezvous_seed = 2;\n}\n\n// This event should be followed by an AccountGroupJoined event\n// This event should be followed by a GroupMemberDeviceAdded event within the AccountGroup\n// This event should be followed by a GroupDeviceChainKeyAdded event within the AccountGroup\n// AccountContactRequestOutgoingEnqueued indicates that the account will attempt to send a contact request when a matching peer is discovered\nmessage AccountContactRequestOutgoingEnqueued {\n  // device_pk is the device sending the event, signs the message\n  bytes device_pk = 1;\n\n  // group_pk is the 1to1 group with the requested user\n  bytes group_pk = 2;\n\n  // contact is a message describing how to connect to the other account\n  ShareableContact contact = 3;\n\n  // own_metadata is the identifying metadata that will be shared to the other account\n  bytes own_metadata = 4;\n}\n\n// AccountContactRequestOutgoingSent indicates that the account has sent a contact request\nmessage AccountContactRequestOutgoingSent {\n  // device_pk is the device sending the account event, signs the message\n  bytes device_pk = 1;\n\n  // contact_pk is the contacted account\n  bytes contact_pk = 2;\n}\n\n// AccountContactRequestIncomingReceived indicates that the account has received a new contact request\nmessage AccountContactRequestIncomingReceived {\n  // device_pk is the device sending the account event (which received the contact request), signs the message\n  bytes device_pk = 1;\n\n  // contact_pk is the account sending the request\n  bytes contact_pk = 2;\n\n  // TODO: is this necessary?\n  // contact_rendezvous_seed is the rendezvous seed of the contact sending the request\n  bytes contact_rendezvous_seed = 3;\n\n  // TODO: is this necessary?\n  // contact_metadata is the metadata specific to the app to identify the contact for the request\n  bytes contact_metadata = 4;\n}\n\n// AccountContactRequestIncomingDiscarded indicates that a contact request has been refused\nmessage AccountContactRequestIncomingDiscarded {\n  // device_pk is the device sending the event, signs the message\n  bytes device_pk = 1;\n\n  // contact_pk is the contact whom request is refused\n  bytes contact_pk = 2;\n}\n\n// This event should be followed by an AccountGroupJoined event\n// This event should be followed by GroupMemberDeviceAdded and GroupDeviceChainKeyAdded events within the AccountGroup\n// AccountContactRequestIncomingAccepted indicates that a contact request has been accepted\nmessage AccountContactRequestIncomingAccepted {\n  // device_pk is the device sending the event, signs the message\n  bytes device_pk = 1;\n\n  // contact_pk is the contact whom request is accepted\n  bytes contact_pk = 2;\n\n  // group_pk is the 1to1 group with the requester user\n  bytes group_pk = 3;\n}\n\n// AccountContactBlocked indicates that a contact is blocked\nmessage AccountContactBlocked {\n  // device_pk is the device sending the event, signs the message\n  bytes device_pk = 1;\n\n  // contact_pk is the contact blocked\n  bytes contact_pk = 2;\n}\n\n// AccountContactUnblocked indicates that a contact is unblocked\nmessage AccountContactUnblocked {\n  // device_pk is the device sending the event, signs the message\n  bytes device_pk = 1;\n\n  // contact_pk is the contact unblocked\n  bytes contact_pk = 2;\n}\n\nmessage GroupReplicating {\n  // device_pk is the device sending the event, signs the message\n  bytes device_pk = 1;\n\n  // authentication_url indicates which server has been used for authentication\n  string authentication_url = 2;\n\n  // replication_server indicates which server will be used for replication\n  string replication_server = 3;\n}\n\n// ***************************************************************************\n//  RPC methods inputs and outputs\n// ***************************************************************************\n\nmessage ServiceExportData {\n  message Request {}\n  message Reply {\n    bytes exported_data = 1;\n  }\n}\n\nmessage ServiceGetConfiguration {\n  enum SettingState {\n    Unknown = 0;\n    Enabled = 1;\n    Disabled = 2;\n    Unavailable = 3;\n  }\n  message Request {}\n  message Reply {\n    // account_pk is the public key of the current account\n    bytes account_pk = 1;\n\n    // device_pk is the public key of the current device\n    bytes device_pk = 2;\n\n    // account_group_pk is the public key of the account group\n    bytes account_group_pk = 3;\n\n    // peer_id is the peer ID of the current IPFS node\n    string peer_id = 4;\n\n    // listeners is the list of swarm listening addresses of the current IPFS node\n    repeated string listeners = 5;\n    SettingState ble_enabled = 6;\n    SettingState wifi_p2p_enabled = 7; // MultiPeerConnectivity for Darwin and Nearby for Android\n    SettingState mdns_enabled = 8;\n    SettingState relay_enabled = 9;\n  }\n}\n\nmessage ContactRequestReference {\n  message Request {}\n  message Reply {\n    // public_rendezvous_seed is the rendezvous seed used by the current account\n    bytes public_rendezvous_seed = 1;\n\n    // enabled indicates if incoming contact requests are enabled\n    bool enabled = 2;\n  }\n}\n\nmessage ContactRequestDisable {\n  message Request {}\n  message Reply {}\n}\n\nmessage ContactRequestEnable {\n  message Request {}\n  message Reply {\n    // public_rendezvous_seed is the rendezvous seed used by the current account\n    bytes public_rendezvous_seed = 1;\n  }\n}\n\nmessage ContactRequestResetReference {\n  message Request {}\n  message Reply {\n    // public_rendezvous_seed is the rendezvous seed used by the current account\n    bytes public_rendezvous_seed = 1;\n  }\n}\n\nmessage ContactRequestSend {\n  message Request {\n    // contact is a message describing how to connect to the other account\n    ShareableContact contact = 1;\n\n    // own_metadata is the identifying metadata that will be shared to the other account\n    bytes own_metadata = 2;\n  }\n  message Reply {}\n}\n\nmessage ContactRequestAccept {\n  message Request {\n    // contact_pk is the identifier of the contact to accept the request from\n    bytes contact_pk = 1;\n  }\n\n  message Reply {}\n}\n\nmessage ContactRequestDiscard {\n  message Request {\n    // contact_pk is the identifier of the contact to ignore the request from\n    bytes contact_pk = 1;\n  }\n\n  message Reply {}\n}\n\nmessage ShareContact {\n  message Request {}\n  message Reply {\n    // encoded_contact is the Protobuf encoding of the ShareableContact. You can further encode the bytes for sharing, such as base58 or QR code.\n    bytes encoded_contact = 1;\n  }\n}\n\nmessage DecodeContact {\n  message Request {\n    // encoded_contact is the Protobuf encoding of the shareable contact (as returned by ShareContact).\n    bytes encoded_contact = 1;\n  }\n  message Reply {\n    // shareable_contact is the decoded shareable contact.\n    ShareableContact contact = 1;\n  }\n}\n\nmessage ContactBlock {\n  message Request {\n    // contact_pk is the identifier of the contact to block\n    bytes contact_pk = 1;\n  }\n\n  message Reply {}\n}\n\nmessage ContactUnblock {\n  message Request {\n    // contact_pk is the identifier of the contact to unblock\n    bytes contact_pk = 1;\n  }\n\n  message Reply {}\n}\n\nmessage ContactAliasKeySend {\n  message Request {\n    // contact_pk is the identifier of the contact to send the alias public key to\n    bytes group_pk = 1;\n  }\n\n  message Reply {}\n}\n\nmessage MultiMemberGroupCreate {\n  message Request {}\n  message Reply {\n    // group_pk is the identifier of the newly created group\n    bytes group_pk = 1;\n  }\n}\n\nmessage MultiMemberGroupJoin {\n  message Request {\n    // group is the information of the group to join\n    Group group = 1;\n  }\n\n  message Reply {}\n}\n\nmessage MultiMemberGroupLeave {\n  message Request {\n    bytes group_pk = 1;\n  }\n\n  message Reply {}\n}\n\nmessage MultiMemberGroupAliasResolverDisclose {\n  message Request {\n    // group_pk is the identifier of the group\n    bytes group_pk = 1;\n  }\n\n  message Reply {}\n}\n\nmessage MultiMemberGroupAdminRoleGrant {\n  message Request {\n    // group_pk is the identifier of the group\n    bytes group_pk = 1;\n\n    // member_pk is the identifier of the member which will be granted the admin role\n    bytes member_pk = 2;\n  }\n\n  message Reply {}\n}\n\nmessage MultiMemberGroupInvitationCreate {\n  message Request {\n    // group_pk is the identifier of the group\n    bytes group_pk = 1;\n  }\n\n  message Reply {\n    // group is the invitation to the group\n    Group group = 1;\n  }\n}\n\nmessage AppMetadataSend {\n  message Request {\n    // group_pk is the identifier of the group\n    bytes group_pk = 1;\n\n    // payload is the payload to send\n    bytes payload = 2;\n\n    // attachment_cids is a list of attachment cids\n    reserved 3; // repeated bytes attachment_cids = 3;\n  }\n\n  message Reply {\n    bytes cid = 1;\n  }\n}\n\nmessage AppMessageSend {\n  message Request {\n    // group_pk is the identifier of the group\n    bytes group_pk = 1;\n\n    // payload is the payload to send\n    bytes payload = 2;\n\n    // attachment_cids is a list of attachment cids\n    reserved 3; // repeated bytes attachment_cids = 3;\n  }\n\n  message Reply {\n    bytes cid = 1;\n  }\n}\n\nmessage GroupMetadataEvent {\n  // event_context contains context information about the event\n  EventContext event_context = 1;\n\n  // metadata contains the newly available metadata\n  GroupMetadata metadata = 2;\n\n  // event_clear clear bytes for the event\n  bytes event = 3;\n}\n\nmessage GroupMessageEvent {\n  // event_context contains context information about the event\n  EventContext event_context = 1;\n\n  // headers contains headers of the secure message\n  MessageHeaders headers = 2;\n\n  // message contains the secure message payload\n  bytes message = 3;\n}\n\nmessage GroupMetadataList {\n  message Request {\n    // group_pk is the identifier of the group\n    bytes group_pk = 1;\n\n    // since is the lower ID bound used to filter events\n    // if not set, will return events since the beginning\n    bytes since_id = 2;\n\n    // since_now will list only new event to come\n    // since_id must not be set\n    bool since_now = 3;\n\n    // until is the upper ID bound used to filter events\n    // if not set, will subscribe to new events to come\n    bytes until_id = 4;\n\n    // until_now will not list new event to come\n    // until_id must not be set\n    bool until_now = 5;\n\n    // reverse_order indicates whether the previous events should be returned in\n    // reverse chronological order\n    bool reverse_order = 6;\n  }\n}\n\nmessage GroupMessageList {\n  message Request {\n    // group_pk is the identifier of the group\n    bytes group_pk = 1;\n\n    // since is the lower ID bound used to filter events\n    // if not set, will return events since the beginning\n    bytes since_id = 2;\n\n    // since_now will list only new event to come\n    // since_id must not be set\n    bool since_now = 3;\n\n    // until is the upper ID bound used to filter events\n    // if not set, will subscribe to new events to come\n    bytes until_id = 4;\n\n    // until_now will not list new event to come\n    // until_id must not be set\n    bool until_now = 5;\n\n    // reverse_order indicates whether the previous events should be returned in\n    // reverse chronological order\n    bool reverse_order = 6;\n  }\n}\n\n\nmessage GroupInfo {\n  message Request {\n    // group_pk is the identifier of the group\n    bytes group_pk = 1;\n\n    // contact_pk is the identifier of the contact\n    bytes contact_pk = 2;\n  }\n\n  message Reply {\n    // group is the group invitation, containing the group pk and its type\n    Group group = 1;\n\n    // member_pk is the identifier of the current member in the group\n    bytes member_pk = 2;\n\n    // device_pk is the identifier of the current device in the group\n    bytes device_pk = 3;\n  }\n}\n\nmessage ActivateGroup {\n  message Request {\n    // group_pk is the identifier of the group\n    bytes group_pk = 1;\n\n    // local_only will open the group without enabling network interactions\n    // with other members\n    bool local_only = 2;\n  }\n\n  message Reply {\n  }\n}\n\nmessage DeactivateGroup {\n  message Request {\n    // group_pk is the identifier of the group\n    bytes group_pk = 1;\n  }\n\n  message Reply {\n  }\n}\n\nmessage GroupDeviceStatus {\n  enum Type {\n    TypeUnknown = 0;\n    TypePeerDisconnected = 1;\n    TypePeerConnected = 2;\n    TypePeerReconnecting = 3;\n  }\n\n  enum Transport {\n    TptUnknown = 0;\n    TptLAN = 1;\n    TptWAN = 2;\n    TptProximity = 3;\n  }\n\n  message Request {\n    bytes group_pk = 1;\n  }\n\n  message Reply {\n    message PeerConnected {\n      string peer_id = 1;\n      bytes device_pk = 2;\n      repeated Transport transports = 3;\n      repeated string maddrs = 4;\n    }\n\n    message PeerReconnecting {\n      string peer_id = 1;\n    }\n\n    message PeerDisconnected {\n      string peer_id = 1;\n    }\n\n    Type type = 1;\n    bytes event = 2;\n  }\n}\n\nmessage DebugListGroups {\n  message Request {\n  }\n\n  message Reply {\n    // group_pk is the public key of the group\n    bytes group_pk = 1;\n\n    // group_type is the type of the group\n    GroupType group_type = 2;\n\n    // contact_pk is the contact public key if appropriate\n    bytes contact_pk = 3;\n  }\n}\n\nmessage DebugInspectGroupStore {\n  message Request {\n    // group_pk is the identifier of the group\n    bytes group_pk = 1;\n\n    // log_type is the log to inspect\n    DebugInspectGroupLogType log_type = 2;\n  }\n\n  message Reply {\n    // cid is the CID of the IPFS log entry\n    bytes cid = 1;\n\n    // parent_cids is the list of the parent entries\n    repeated bytes parent_cids = 2 ;\n\n    // event_type metadata event type if subscribed to metadata events\n    EventType metadata_event_type = 3;\n\n    // device_pk is the public key of the device signing the entry\n    bytes device_pk = 4;\n\n    // payload is the un encrypted entry payload if available\n    bytes payload = 6;\n  }\n}\n\nmessage DebugGroup {\n  message Request {\n    // group_pk is the identifier of the group\n    bytes group_pk = 1;\n  }\n\n  message Reply {\n    // peer_ids is the list of peer ids connected to the same group\n    repeated string peer_ids = 1 ;\n  }\n}\n\nenum DebugInspectGroupLogType {\n  DebugInspectGroupLogTypeUndefined = 0;\n  DebugInspectGroupLogTypeMessage = 1;\n  DebugInspectGroupLogTypeMetadata = 2;\n\n}\n\nenum ContactState {\n  ContactStateUndefined = 0;\n  ContactStateToRequest = 1;\n  ContactStateReceived = 2;\n  ContactStateAdded = 3;\n  ContactStateRemoved = 4;\n  ContactStateDiscarded = 5;\n  ContactStateBlocked = 6;\n}\n\nmessage ShareableContact {\n  // pk is the account to send a contact request to\n  bytes pk = 1;\n\n  // public_rendezvous_seed is the rendezvous seed used by the account to send a contact request to\n  bytes public_rendezvous_seed = 2;\n\n  // metadata is the metadata specific to the app to identify the contact for the request\n  bytes metadata = 3;\n}\n\nmessage ServiceTokenSupportedService {\n  string service_type = 1;\n  string service_endpoint = 2;\n}\n\nmessage ServiceToken {\n  string token = 1;\n  string authentication_url = 2 ;\n  repeated ServiceTokenSupportedService supported_services = 3;\n  int64 expiration = 4;\n}\n\nmessage CredentialVerificationServiceInitFlow {\n  message Request {\n    string service_url = 1;\n    bytes public_key = 2;\n    string link = 3;\n  }\n  message Reply {\n    string url = 1;\n    bool secure_url = 2;\n  }\n}\n\nmessage CredentialVerificationServiceCompleteFlow {\n  message Request {\n    string callback_uri = 1;\n  }\n  message Reply {\n    string identifier = 1;\n  }\n}\n\nmessage VerifiedCredentialsList {\n  message Request {\n    string filter_identifier = 1;\n    string filter_issuer = 2;\n    bool exclude_expired = 3;\n  }\n  message Reply {\n    AccountVerifiedCredentialRegistered credential = 1;\n  }\n}\n\nmessage ReplicationServiceRegisterGroup {\n  message Request{\n    bytes group_pk = 1;\n    string token = 2;\n    string authentication_url = 3;\n    string replication_server = 4;\n  }\n  message Reply{}\n}\n\nmessage ReplicationServiceReplicateGroup {\n  message Request {\n    Group group = 1;\n  }\n  message Reply {\n    bool ok = 1;\n  }\n}\n\nmessage SystemInfo {\n  message Request {}\n  message Reply {\n    Process process = 1;\n    P2P p2p = 2;\n    OrbitDB orbitdb = 3;\n    repeated string warns = 4;\n  }\n\n  message OrbitDB {\n    ReplicationStatus account_metadata = 1;\n\n    message ReplicationStatus {\n      int64 progress = 1;\n      int64 maximum = 2;\n      int64 buffered = 3;\n      int64 queued = 4;\n    }\n  }\n\n  message P2P {\n    int64 connected_peers = 1;\n  }\n  message Process {\n    string version = 1;\n    string vcs_ref = 2;\n    int64 uptime_ms = 3;\n    int64 user_cpu_time_ms = 10;\n    int64 system_cpu_time_ms = 11;\n    int64 started_at = 12;\n    uint64 rlimit_cur = 13;\n    int64 num_goroutine = 14;\n    int64 nofile = 15;\n    bool too_many_open_files = 16;\n    int64 num_cpu = 17;\n    string go_version = 18;\n    string operating_system = 19;\n    string host_name = 20;\n    string arch = 21;\n    uint64 rlimit_max = 22;\n    int64 pid = 23;\n    int64 ppid = 24;\n    int64 priority = 25;\n    int64 uid = 26;\n    string working_dir = 27;\n    string system_username = 28;\n  }\n}\n\nmessage PeerList {\n  message Request {}\n  message Reply {\n    repeated Peer peers = 1;\n  }\n\n  message Peer {\n    // id is the libp2p.PeerID.\n    string id = 1;\n\n    // routes are the list of active and known maddr.\n    repeated Route routes = 2;\n\n    // errors is a list of errors related to the peer.\n    repeated string errors = 3;\n\n    // Features is a list of available features.\n    repeated Feature features = 4;\n\n    // MinLatency is the minimum latency across all the peer routes.\n    int64 min_latency = 5;\n\n    // IsActive is true if at least one of the route is active.\n    bool is_active = 6;\n\n    // Direction is the aggregate of all the routes's direction.\n    Direction direction = 7;\n  }\n\n  message Route {\n    // IsActive indicates whether the address is currently used or just known.\n    bool is_active = 1;\n\n    // Address is the multiaddress via which we are connected with the peer.\n    string address = 2;\n\n    // Direction is which way the connection was established.\n    Direction direction = 3;\n\n    // Latency is the last known round trip time to the peer in ms.\n    int64 latency = 4;\n\n    // Streams returns list of streams established with the peer.\n    repeated Stream streams = 5;\n  }\n\n  message Stream {\n    // id is an identifier used to write protocol headers in streams.\n    string id = 1;\n  }\n\n  enum Feature {\n    UnknownFeature = 0;\n    WeshFeature = 1;\n    BLEFeature = 2;\n    LocalFeature = 3;\n    TorFeature = 4;\n    QuicFeature = 5;\n\n  }\n}\n\nenum Direction {\n  UnknownDir = 0;\n  InboundDir = 1;\n  OutboundDir = 2;\n  BiDir = 3;\n}\n\n// Progress define a generic object that can be used to display a progress bar for long-running actions.\nmessage Progress {\n  string state = 1;\n  string doing = 2;\n  float progress = 3;\n  uint64 completed = 4;\n  uint64 total = 5;\n  uint64 delay = 6;\n}\n\nmessage OutOfStoreMessage {\n  bytes cid = 1;\n  bytes device_pk = 2;\n  fixed64 counter = 3;\n  bytes sig = 4;\n  fixed32 flags = 5;\n  bytes encrypted_payload = 6;\n  bytes nonce = 7;\n}\n\nmessage OutOfStoreMessageEnvelope {\n  bytes nonce = 1;\n  bytes box = 2;\n  bytes group_reference = 3;\n}\n\nmessage OutOfStoreReceive {\n  message Request {\n    bytes payload = 1;\n  }\n  message Reply {\n    OutOfStoreMessage message = 1;\n    bytes cleartext = 2;\n    bytes group_public_key = 3;\n    bool already_received = 4;\n  }\n}\n\nmessage OutOfStoreSeal {\n  message Request {\n    bytes cid = 1;\n    bytes group_public_key = 2;\n  }\n  message Reply {\n    bytes encrypted = 1;\n  }\n}\n\nmessage AccountVerifiedCredentialRegistered {\n  // device_pk is the public key of the device sending the message\n  bytes device_pk = 1;\n\n  bytes signed_identity_public_key = 2;\n\n  string verified_credential = 3;\n\n  int64 registration_date = 4;\n\n  int64 expiration_date = 5;\n\n  string identifier = 6;\n\n  string issuer = 7;\n}\n\nmessage FirstLastCounters {\n  uint64 first = 1;\n  uint64 last = 2;\n}\n\n// OrbitDBMessageHeads is the payload sent on orbitdb to share peer's heads\nmessage OrbitDBMessageHeads {\n  message Box {\n    string address = 1;\n    bytes heads = 2;\n    bytes device_pk = 3;\n    bytes peer_id = 4;\n  }\n\n  // sealed box should contain encrypted Box\n  bytes sealed_box = 2;\n\n  // current topic used\n  bytes raw_rotation = 3;\n}\n\nmessage RefreshContactRequest {\n  message Peer {\n    // id is the libp2p.PeerID.\n    string id = 1;\n\n    // list of peers multiaddrs.\n    repeated string addrs = 2;\n  }\n\n  message Request {\n    bytes contact_pk = 1;\n    // timeout in second\n    int64 timeout = 2;\n  }\n\n  message Reply {\n    // peers found and successfully connected.\n    repeated Peer peers_found = 1;\n  }\n}\n"
  },
  {
    "path": "api/protocol/replicationtypes/bertyreplication.proto",
    "content": "syntax = \"proto3\";\n\npackage weshnet.replication.v1;\n\nimport \"protocoltypes.proto\";\nimport \"tagger/tagger.proto\";\n\noption go_package = \"berty.tech/weshnet/v2/pkg/replicationtypes\";\n\n// ReplicationService\nservice ReplicationService {\n  // ReplicateGroup\n  rpc ReplicateGroup(ReplicationServiceReplicateGroup.Request) returns (ReplicationServiceReplicateGroup.Reply);\n\n  rpc ReplicateGlobalStats(ReplicateGlobalStats.Request) returns (ReplicateGlobalStats.Reply);\n\n  rpc ReplicateGroupStats(ReplicateGroupStats.Request) returns (ReplicateGroupStats.Reply);\n}\n\nmessage ReplicatedGroup {\n  string public_key = 1 [(tagger.tags) = \"gorm:\\\"primaryKey\\\"\"];\n  string sign_pub = 2;\n  string link_key = 3;\n  int64 created_at = 100;\n  int64 updated_at = 101;\n  int64 metadata_entries_count = 102;\n  string metadata_latest_head = 103;\n  int64 message_entries_count = 104;\n  string message_latest_head = 105;\n}\n\nmessage ReplicatedGroupToken {\n  string replicated_group_public_key = 1 [(tagger.tags) = \"gorm:\\\"index;primaryKey;autoIncrement:false\\\"\"];\n  ReplicatedGroup replicated_group = 2;\n  string token_issuer = 3 [(tagger.tags) = \"gorm:\\\"primaryKey;autoIncrement:false\\\"\"];\n  string token_id = 4 [(tagger.tags) = \"gorm:\\\"primaryKey;autoIncrement:false\\\"\"];\n  int64 created_at = 5;\n}\n\nmessage ReplicationServiceReplicateGroup {\n  message Request {\n    weshnet.protocol.v1.Group group = 1;\n  }\n  message Reply {\n    bool ok = 1;\n  }\n}\n\nmessage ReplicateGlobalStats {\n  message Request {}\n  message Reply {\n    int64 started_at = 1;\n    int64 replicated_groups = 2;\n    int64 total_metadata_entries = 3;\n    int64 total_message_entries = 4;\n  }\n}\n\nmessage ReplicateGroupStats {\n  message Request {\n    string group_public_key = 1;\n  }\n  message Reply {\n    ReplicatedGroup group = 1;\n  }\n}\n"
  },
  {
    "path": "api/protocol/verifiablecredstypes/bertyverifiablecreds.proto",
    "content": "syntax = \"proto3\";\n\npackage weshnet.account.v1;\n\noption go_package = \"berty.tech/weshnet/v2/pkg/verifiablecredstypes\";\n\n// StateChallenge serialized and signed state used when requesting a challenge\nmessage StateChallenge {\n  bytes timestamp = 1;\n  bytes nonce = 2;\n  string berty_link = 3;\n  string redirect_uri = 4;\n  string state = 5;\n}\n\n// StateCode serialized and signed state used when requesting a code\nmessage StateCode {\n  bytes timestamp = 1;\n  string berty_link = 2;\n  CodeStrategy code_strategy = 3;\n  string identifier = 4;\n  string code = 5;\n  string redirect_uri = 6;\n  string state = 7;\n}\n\nmessage AccountCryptoChallenge {\n  string challenge = 1;\n}\n\nenum FlowType {\n  FlowTypeUndefined = 0;\n  // FlowTypeCode asks users a code sent on a side channel\n  FlowTypeCode = 1;\n  // FlowTypeAuth currently unimplemented\n  FlowTypeAuth = 2;\n  // FlowTypeProof currently unimplemented\n  FlowTypeProof = 3;\n}\n\nenum CodeStrategy {\n  CodeStrategyUndefined = 0;\n  // CodeStrategy6Digits currently unimplemented\n  CodeStrategy6Digits = 1;\n  // CodeStrategy10Chars currently unimplemented\n  CodeStrategy10Chars = 2;\n  // CodeStrategyMocked6Zeroes must only be used in testing\n  CodeStrategyMocked6Zeroes = 999;\n}\n"
  },
  {
    "path": "api_app.go",
    "content": "package weshnet\n\nimport (\n\t\"context\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\n\t\"github.com/ipfs/go-cid\"\n\t\"go.uber.org/zap\"\n\t\"google.golang.org/protobuf/proto\"\n\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n\t\"berty.tech/weshnet/v2/pkg/tyber\"\n)\n\nfunc (s *service) AppMetadataSend(ctx context.Context, req *protocoltypes.AppMetadataSend_Request) (_ *protocoltypes.AppMetadataSend_Reply, err error) {\n\tctx, _, endSection := tyber.Section(ctx, s.logger, fmt.Sprintf(\"Sending app metadata to group %s\", base64.RawURLEncoding.EncodeToString(req.GroupPk)))\n\tdefer func() { endSection(err, \"\") }()\n\n\tgc, err := s.GetContextGroupForID(req.GroupPk)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrGroupMissing.Wrap(err)\n\t}\n\ttyberLogGroupContext(ctx, s.logger, gc)\n\n\top, err := gc.MetadataStore().SendAppMetadata(ctx, req.Payload)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrOrbitDBAppend.Wrap(err)\n\t}\n\n\treturn &protocoltypes.AppMetadataSend_Reply{Cid: op.GetEntry().GetHash().Bytes()}, nil\n}\n\nfunc (s *service) AppMessageSend(ctx context.Context, req *protocoltypes.AppMessageSend_Request) (_ *protocoltypes.AppMessageSend_Reply, err error) {\n\tctx, _, endSection := tyber.Section(ctx, s.logger, fmt.Sprintf(\"Sending message to group %s\", base64.RawURLEncoding.EncodeToString(req.GroupPk)))\n\tdefer func() { endSection(err, \"\") }()\n\n\tgc, err := s.GetContextGroupForID(req.GroupPk)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrGroupMissing.Wrap(err)\n\t}\n\ttyberLogGroupContext(ctx, s.logger, gc)\n\n\top, err := gc.MessageStore().AddMessage(ctx, req.Payload)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrOrbitDBAppend.Wrap(err)\n\t}\n\n\treturn &protocoltypes.AppMessageSend_Reply{Cid: op.GetEntry().GetHash().Bytes()}, nil\n}\n\n// OutOfStoreReceive parses a payload received outside a synchronized store\nfunc (s *service) OutOfStoreReceive(ctx context.Context, request *protocoltypes.OutOfStoreReceive_Request) (*protocoltypes.OutOfStoreReceive_Reply, error) {\n\toutOfStoreMessage, group, clearPayload, alreadyDecrypted, err := s.secretStore.OpenOutOfStoreMessage(ctx, request.Payload)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrCryptoDecrypt.Wrap(err)\n\t}\n\n\treturn &protocoltypes.OutOfStoreReceive_Reply{\n\t\tMessage:         outOfStoreMessage,\n\t\tCleartext:       clearPayload,\n\t\tGroupPublicKey:  group.PublicKey,\n\t\tAlreadyReceived: alreadyDecrypted,\n\t}, nil\n}\n\n// OutOfStoreSeal creates a payload of a message present in store to be sent outside a synchronized store\nfunc (s *service) OutOfStoreSeal(ctx context.Context, request *protocoltypes.OutOfStoreSeal_Request) (*protocoltypes.OutOfStoreSeal_Reply, error) {\n\tgc, err := s.GetContextGroupForID(request.GroupPublicKey)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t_, c, err := cid.CidFromBytes(request.Cid)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(err)\n\t}\n\n\tsealedMessageEnvelope, err := gc.messageStore.GetOutOfStoreMessageEnvelope(ctx, c)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\n\tsealedMessageEnvelopeBytes, err := proto.Marshal(sealedMessageEnvelope)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\treturn &protocoltypes.OutOfStoreSeal_Reply{\n\t\tEncrypted: sealedMessageEnvelopeBytes,\n\t}, nil\n}\n\nfunc tyberLogGroupContext(ctx context.Context, logger *zap.Logger, gc *GroupContext) {\n\tmemberPK, err := gc.MemberPubKey().Raw()\n\tif err != nil {\n\t\tmemberPK = []byte{}\n\t}\n\n\tlogger.Debug(\"Got group context\", tyber.FormatStepLogFields(ctx, []tyber.Detail{\n\t\t{Name: \"GroupType\", Description: gc.Group().GetGroupType().String()},\n\t\t{Name: \"GroupPK\", Description: base64.RawURLEncoding.EncodeToString(gc.Group().PublicKey)},\n\t\t{Name: \"MemberPK\", Description: base64.RawURLEncoding.EncodeToString(memberPK)},\n\t})...)\n}\n"
  },
  {
    "path": "api_client.go",
    "content": "package weshnet\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"sync\"\n\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n\t\"berty.tech/weshnet/v2/pkg/tyber\"\n)\n\nfunc (s *service) ServiceExportData(_ *protocoltypes.ServiceExportData_Request, server protocoltypes.ProtocolService_ServiceExportDataServer) (err error) {\n\tctx, _, endSection := tyber.Section(server.Context(), s.logger, \"Exporting protocol instance data\")\n\tdefer func() { endSection(err, \"\") }()\n\n\tr, w := io.Pipe()\n\n\tvar exportErr error\n\twg := sync.WaitGroup{}\n\twg.Add(1)\n\n\tgo func() {\n\t\tdefer func() { _ = r.Close() }()\n\t\tdefer wg.Done()\n\n\t\tfor {\n\t\t\tcontents := make([]byte, 4096)\n\t\t\tl, err := r.Read(contents)\n\n\t\t\tif err == io.EOF {\n\t\t\t\tbreak\n\t\t\t} else if err != nil {\n\t\t\t\texportErr = errcode.ErrCode_ErrStreamRead.Wrap(err)\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tif err := server.Send(&protocoltypes.ServiceExportData_Reply{ExportedData: contents[:l]}); err != nil {\n\t\t\t\texportErr = errcode.ErrCode_ErrStreamWrite.Wrap(err)\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}()\n\n\tif err := s.export(ctx, w); err != nil {\n\t\treturn errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\t_ = w.Close()\n\n\twg.Wait()\n\n\tif exportErr != nil {\n\t\treturn exportErr\n\t}\n\n\treturn nil\n}\n\nfunc (s *service) ServiceGetConfiguration(ctx context.Context, _ *protocoltypes.ServiceGetConfiguration_Request) (*protocoltypes.ServiceGetConfiguration_Reply, error) {\n\tkey, err := s.ipfsCoreAPI.Key().Self(ctx)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_TODO.Wrap(err)\n\t}\n\n\tmaddrs, err := s.ipfsCoreAPI.Swarm().ListenAddrs(ctx)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_TODO.Wrap(err)\n\t}\n\n\tlisteners := make([]string, len(maddrs))\n\tfor i, addr := range maddrs {\n\t\tlisteners[i] = addr.String()\n\t}\n\n\taccountGroup := s.getAccountGroup()\n\tif accountGroup == nil {\n\t\treturn nil, errcode.ErrCode_ErrGroupMissing\n\t}\n\n\tmember, err := accountGroup.MemberPubKey().Raw()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\tdevice, err := accountGroup.DevicePubKey().Raw()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\treturn &protocoltypes.ServiceGetConfiguration_Reply{\n\t\tAccountPk:      member,\n\t\tDevicePk:       device,\n\t\tAccountGroupPk: accountGroup.Group().PublicKey,\n\t\tPeerId:         key.ID().String(),\n\t\tListeners:      listeners,\n\t}, nil\n}\n"
  },
  {
    "path": "api_contact.go",
    "content": "package weshnet\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/libp2p/go-libp2p/core/crypto\"\n\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n\t\"berty.tech/weshnet/v2/pkg/tyber\"\n)\n\nfunc (s *service) ContactAliasKeySend(ctx context.Context, req *protocoltypes.ContactAliasKeySend_Request) (_ *protocoltypes.ContactAliasKeySend_Reply, err error) {\n\tctx, _, endSection := tyber.Section(ctx, s.logger, \"Sending contact alias key\")\n\tdefer func() { endSection(err, \"\") }()\n\n\tg, err := s.GetContextGroupForID(req.GroupPk)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrGroupMissing.Wrap(err)\n\t}\n\n\tif _, err := g.MetadataStore().ContactSendAliasKey(ctx); err != nil {\n\t\treturn nil, errcode.ErrCode_ErrOrbitDBAppend.Wrap(err)\n\t}\n\n\treturn &protocoltypes.ContactAliasKeySend_Reply{}, nil\n}\n\nfunc (s *service) ContactBlock(ctx context.Context, req *protocoltypes.ContactBlock_Request) (_ *protocoltypes.ContactBlock_Reply, err error) {\n\tctx, _, endSection := tyber.Section(ctx, s.logger, \"Blocking contact\")\n\tdefer func() { endSection(err, \"\") }()\n\n\tpk, err := crypto.UnmarshalEd25519PublicKey(req.ContactPk)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\tif _, err := s.getAccountGroup().MetadataStore().ContactBlock(ctx, pk); err != nil {\n\t\treturn nil, errcode.ErrCode_ErrOrbitDBAppend.Wrap(err)\n\t}\n\n\treturn &protocoltypes.ContactBlock_Reply{}, nil\n}\n\nfunc (s *service) ContactUnblock(ctx context.Context, req *protocoltypes.ContactUnblock_Request) (_ *protocoltypes.ContactUnblock_Reply, err error) {\n\tctx, _, endSection := tyber.Section(ctx, s.logger, \"Unblocking contact\")\n\tdefer func() { endSection(err, \"\") }()\n\n\tpk, err := crypto.UnmarshalEd25519PublicKey(req.ContactPk)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\tif _, err := s.getAccountGroup().MetadataStore().ContactUnblock(ctx, pk); err != nil {\n\t\treturn nil, errcode.ErrCode_ErrOrbitDBAppend.Wrap(err)\n\t}\n\n\treturn &protocoltypes.ContactUnblock_Reply{}, nil\n}\n\nfunc (s *service) RefreshContactRequest(ctx context.Context, req *protocoltypes.RefreshContactRequest_Request) (*protocoltypes.RefreshContactRequest_Reply, error) {\n\tif len(req.ContactPk) == 0 {\n\t\treturn nil, errcode.ErrCode_ErrInternal\n\t}\n\n\tvar cancel context.CancelFunc\n\tif req.Timeout > 0 {\n\t\tctx, cancel = context.WithTimeout(ctx, time.Duration(req.Timeout)*time.Second)\n\t} else {\n\t\tctx, cancel = context.WithCancel(ctx)\n\t}\n\tdefer cancel()\n\n\tkey := string(req.ContactPk)\n\ts.muRefreshprocess.Lock()\n\tif clfn, ok := s.refreshprocess[key]; ok {\n\t\tclfn() // close previous refresh method\n\t}\n\ts.refreshprocess[key] = cancel\n\ts.muRefreshprocess.Unlock()\n\n\tpeers, err := s.swiper.RefreshContactRequest(ctx, req.ContactPk)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to refresh group: %w\", err)\n\t}\n\n\tres := &protocoltypes.RefreshContactRequest_Reply{\n\t\tPeersFound: []*protocoltypes.RefreshContactRequest_Peer{},\n\t}\n\tfor _, p := range peers {\n\t\t// check if we can connect to this peers\n\t\tif err := s.host.Connect(ctx, p); err != nil {\n\t\t\tcontinue\n\t\t}\n\n\t\taddrs := make([]string, len(p.Addrs))\n\t\tfor i, addr := range p.Addrs {\n\t\t\taddrs[i] = addr.String()\n\t\t}\n\n\t\tres.PeersFound = append(res.PeersFound, &protocoltypes.RefreshContactRequest_Peer{\n\t\t\tId:    p.ID.String(),\n\t\t\tAddrs: addrs,\n\t\t})\n\t}\n\n\treturn res, nil\n}\n"
  },
  {
    "path": "api_contact_request_test.go",
    "content": "package weshnet\n\nimport (\n\t\"context\"\n\t\"testing\"\n\t\"time\"\n\n\tmocknet \"github.com/libp2p/go-libp2p/p2p/net/mock\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n\t\"berty.tech/weshnet/v2/pkg/testutil\"\n)\n\nfunc TestShareContact(t *testing.T) {\n\tctx, cancel := context.WithTimeout(context.Background(), time.Second*20)\n\tdefer cancel()\n\tlogger, cleanup := testutil.Logger(t)\n\tdefer cleanup()\n\n\topts := TestingOpts{\n\t\tMocknet: mocknet.New(),\n\t\tLogger:  logger,\n\t}\n\n\tpts, cleanup := NewTestingProtocolWithMockedPeers(ctx, t, &opts, nil, 2)\n\tdefer cleanup()\n\n\tbinaryContact, err := pts[0].Client.ShareContact(ctx, &protocoltypes.ShareContact_Request{})\n\trequire.NoError(t, err)\n\n\t// Check that ShareContact reset the contact request reference and enabled contact requests.\n\tcontactRequestRef, err := pts[0].Client.ContactRequestReference(ctx,\n\t\t&protocoltypes.ContactRequestReference_Request{})\n\trequire.NoError(t, err)\n\n\trequire.NotEqual(t, 0, len(contactRequestRef.PublicRendezvousSeed))\n\trequire.Equal(t, true, contactRequestRef.Enabled)\n\n\t// Decode.\n\tcontact, err := pts[0].Client.DecodeContact(ctx, &protocoltypes.DecodeContact_Request{\n\t\tEncodedContact: binaryContact.EncodedContact,\n\t})\n\trequire.NoError(t, err)\n\n\t// Check for the expected info.\n\tconfig, err := pts[0].Client.ServiceGetConfiguration(ctx,\n\t\t&protocoltypes.ServiceGetConfiguration_Request{})\n\trequire.NoError(t, err)\n\trequire.Equal(t, contact.Contact.Pk, config.AccountPk)\n\trequire.Equal(t, contact.Contact.PublicRendezvousSeed, contactRequestRef.PublicRendezvousSeed)\n}\n"
  },
  {
    "path": "api_contactrequest.go",
    "content": "package weshnet\n\nimport (\n\t\"context\"\n\n\t\"github.com/libp2p/go-libp2p/core/crypto\"\n\t\"google.golang.org/protobuf/proto\"\n\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n\t\"berty.tech/weshnet/v2/pkg/tyber\"\n)\n\n// ContactRequestReference retrieves the necessary information to create a contact link\nfunc (s *service) ContactRequestReference(context.Context, *protocoltypes.ContactRequestReference_Request) (*protocoltypes.ContactRequestReference_Reply, error) {\n\taccountGroup := s.getAccountGroup()\n\tif accountGroup == nil {\n\t\treturn nil, errcode.ErrCode_ErrGroupMissing\n\t}\n\n\tenabled, shareableContact := accountGroup.MetadataStore().GetIncomingContactRequestsStatus()\n\trdvSeed := []byte(nil)\n\n\tif shareableContact != nil {\n\t\trdvSeed = shareableContact.PublicRendezvousSeed\n\t}\n\n\treturn &protocoltypes.ContactRequestReference_Reply{\n\t\tPublicRendezvousSeed: rdvSeed,\n\t\tEnabled:              enabled,\n\t}, nil\n}\n\n// ContactRequestDisable disables incoming contact requests\nfunc (s *service) ContactRequestDisable(ctx context.Context, _ *protocoltypes.ContactRequestDisable_Request) (_ *protocoltypes.ContactRequestDisable_Reply, err error) {\n\tctx, _, endSection := tyber.Section(ctx, s.logger, \"Disabling contact requests\")\n\tdefer func() { endSection(err, \"\") }()\n\n\taccountGroup := s.getAccountGroup()\n\tif accountGroup == nil {\n\t\treturn nil, errcode.ErrCode_ErrGroupMissing\n\t}\n\n\tif _, err := accountGroup.MetadataStore().ContactRequestDisable(ctx); err != nil {\n\t\treturn nil, errcode.ErrCode_ErrOrbitDBAppend.Wrap(err)\n\t}\n\n\treturn &protocoltypes.ContactRequestDisable_Reply{}, nil\n}\n\n// ContactRequestEnable enables incoming contact requests\nfunc (s *service) ContactRequestEnable(ctx context.Context, _ *protocoltypes.ContactRequestEnable_Request) (_ *protocoltypes.ContactRequestEnable_Reply, err error) {\n\tctx, _, endSection := tyber.Section(ctx, s.logger, \"Enabling contact requests\")\n\tdefer func() { endSection(err, \"\") }()\n\n\taccountGroup := s.getAccountGroup()\n\tif accountGroup == nil {\n\t\treturn nil, errcode.ErrCode_ErrGroupMissing\n\t}\n\n\tif _, err := accountGroup.MetadataStore().ContactRequestEnable(ctx); err != nil {\n\t\treturn nil, errcode.ErrCode_ErrOrbitDBAppend.Wrap(err)\n\t}\n\n\t_, shareableContact := accountGroup.MetadataStore().GetIncomingContactRequestsStatus()\n\trdvSeed := []byte(nil)\n\n\tif shareableContact != nil {\n\t\trdvSeed = shareableContact.PublicRendezvousSeed\n\t}\n\n\treturn &protocoltypes.ContactRequestEnable_Reply{\n\t\tPublicRendezvousSeed: rdvSeed,\n\t}, nil\n}\n\n// ContactRequestResetReference generates a new contact request reference\nfunc (s *service) ContactRequestResetReference(ctx context.Context, _ *protocoltypes.ContactRequestResetReference_Request) (_ *protocoltypes.ContactRequestResetReference_Reply, err error) {\n\tctx, _, endSection := tyber.Section(ctx, s.logger, \"Resetting contact requests reference\")\n\tdefer func() { endSection(err, \"\") }()\n\n\taccountGroup := s.getAccountGroup()\n\tif accountGroup == nil {\n\t\treturn nil, errcode.ErrCode_ErrGroupMissing\n\t}\n\n\tif _, err := accountGroup.MetadataStore().ContactRequestReferenceReset(ctx); err != nil {\n\t\treturn nil, errcode.ErrCode_ErrOrbitDBAppend.Wrap(err)\n\t}\n\n\t_, shareableContact := accountGroup.MetadataStore().GetIncomingContactRequestsStatus()\n\trdvSeed := []byte(nil)\n\n\tif shareableContact != nil {\n\t\trdvSeed = shareableContact.PublicRendezvousSeed\n\t}\n\n\treturn &protocoltypes.ContactRequestResetReference_Reply{\n\t\tPublicRendezvousSeed: rdvSeed,\n\t}, nil\n}\n\n// ContactRequestSend enqueues a new contact request to be sent\nfunc (s *service) ContactRequestSend(ctx context.Context, req *protocoltypes.ContactRequestSend_Request) (_ *protocoltypes.ContactRequestSend_Reply, err error) {\n\tctx, _, endSection := tyber.Section(ctx, s.logger, \"Sending contact request\")\n\tdefer func() { endSection(err, \"\") }()\n\n\ts.logger.Debug(\"Contact request info\", tyber.FormatStepLogFields(ctx, []tyber.Detail{}, tyber.WithJSONDetail(\"Request\", req))...)\n\n\tshareableContact := req.Contact\n\tif shareableContact == nil {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput\n\t}\n\n\taccountGroup := s.getAccountGroup()\n\tif accountGroup == nil {\n\t\treturn nil, errcode.ErrCode_ErrGroupMissing\n\t}\n\n\tif _, err := accountGroup.MetadataStore().ContactRequestOutgoingEnqueue(ctx, shareableContact, req.OwnMetadata); err != nil {\n\t\treturn nil, errcode.ErrCode_ErrOrbitDBAppend.Wrap(err)\n\t}\n\n\treturn &protocoltypes.ContactRequestSend_Reply{}, nil\n}\n\n// ContactRequestAccept accepts a contact request\nfunc (s *service) ContactRequestAccept(ctx context.Context, req *protocoltypes.ContactRequestAccept_Request) (_ *protocoltypes.ContactRequestAccept_Reply, err error) {\n\tctx, _, endSection := tyber.Section(ctx, s.logger, \"Accepting contact request\")\n\tdefer func() { endSection(err, \"\") }()\n\n\tpk, err := crypto.UnmarshalEd25519PublicKey(req.ContactPk)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\tgroup, err := s.secretStore.GetGroupForContact(pk)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\n\taccountGroup := s.getAccountGroup()\n\tif accountGroup == nil {\n\t\treturn nil, errcode.ErrCode_ErrGroupMissing\n\t}\n\n\tif _, err := accountGroup.MetadataStore().ContactRequestIncomingAccept(ctx, pk); err != nil {\n\t\treturn nil, errcode.ErrCode_ErrOrbitDBAppend.Wrap(err)\n\t}\n\n\tif err = s.secretStore.PutGroup(ctx, group); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &protocoltypes.ContactRequestAccept_Reply{}, nil\n}\n\n// ContactRequestDiscard ignores a contact request without informing the request sender\nfunc (s *service) ContactRequestDiscard(ctx context.Context, req *protocoltypes.ContactRequestDiscard_Request) (_ *protocoltypes.ContactRequestDiscard_Reply, err error) {\n\tctx, _, endSection := tyber.Section(ctx, s.logger, \"Discarding contact request\")\n\tdefer func() { endSection(err, \"\") }()\n\n\tpk, err := crypto.UnmarshalEd25519PublicKey(req.ContactPk)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\taccountGroup := s.getAccountGroup()\n\tif accountGroup == nil {\n\t\treturn nil, errcode.ErrCode_ErrGroupMissing\n\t}\n\n\tif _, err := accountGroup.MetadataStore().ContactRequestIncomingDiscard(ctx, pk); err != nil {\n\t\treturn nil, errcode.ErrCode_ErrOrbitDBAppend.Wrap(err)\n\t}\n\n\treturn &protocoltypes.ContactRequestDiscard_Reply{}, nil\n}\n\n// ShareContact uses ContactRequestReference to get the contact information for the current account and\n// returns the Protobuf encoding which you can further encode and share. If needed, his will reset the\n// contact request reference and enable contact requests.\nfunc (s *service) ShareContact(ctx context.Context, _ *protocoltypes.ShareContact_Request) (_ *protocoltypes.ShareContact_Reply, err error) {\n\taccountGroup := s.getAccountGroup()\n\tif accountGroup == nil {\n\t\treturn nil, errcode.ErrCode_ErrGroupMissing\n\t}\n\n\tenabled, shareableContact := accountGroup.MetadataStore().GetIncomingContactRequestsStatus()\n\trdvSeed := []byte(nil)\n\n\tif shareableContact != nil {\n\t\trdvSeed = shareableContact.PublicRendezvousSeed\n\t}\n\n\tif !enabled || len(rdvSeed) == 0 {\n\t\t// We need to enable and reset the contact request reference.\n\t\tif _, err := accountGroup.MetadataStore().ContactRequestEnable(ctx); err != nil {\n\t\t\treturn nil, errcode.ErrCode_ErrOrbitDBAppend.Wrap(err)\n\t\t}\n\n\t\tif _, err := accountGroup.MetadataStore().ContactRequestReferenceReset(ctx); err != nil {\n\t\t\treturn nil, errcode.ErrCode_ErrOrbitDBAppend.Wrap(err)\n\t\t}\n\n\t\t// Refresh the info.\n\t\t_, shareableContact = accountGroup.MetadataStore().GetIncomingContactRequestsStatus()\n\t\trdvSeed = []byte(nil)\n\n\t\tif shareableContact != nil {\n\t\t\trdvSeed = shareableContact.PublicRendezvousSeed\n\t\t}\n\t}\n\n\t// Get the client's AccountPK.\n\tmember, err := accountGroup.MemberPubKey().Raw()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\tencodedContact, err := proto.Marshal(&protocoltypes.ShareableContact{\n\t\tPk:                   member,\n\t\tPublicRendezvousSeed: rdvSeed,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &protocoltypes.ShareContact_Reply{\n\t\tEncodedContact: encodedContact,\n\t}, nil\n}\n\n// DecodeContact decodes the Protobuf encoding of a shareable contact which was returned by ShareContact.\nfunc (s *service) DecodeContact(_ context.Context, req *protocoltypes.DecodeContact_Request) (_ *protocoltypes.DecodeContact_Reply, err error) {\n\tcontact := &protocoltypes.ShareableContact{}\n\tif err := proto.Unmarshal(req.EncodedContact, contact); err != nil {\n\t\tpanic(err)\n\t}\n\n\treturn &protocoltypes.DecodeContact_Reply{\n\t\tContact: contact,\n\t}, nil\n}\n"
  },
  {
    "path": "api_debug.go",
    "content": "package weshnet\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/libp2p/go-libp2p/core/crypto\"\n\t\"github.com/libp2p/go-libp2p/core/network\"\n\tpeer \"github.com/libp2p/go-libp2p/core/peer\"\n\t\"go.uber.org/multierr\"\n\t\"go.uber.org/zap\"\n\t\"google.golang.org/protobuf/proto\"\n\n\t\"berty.tech/go-orbit-db/stores/operation\"\n\t\"berty.tech/weshnet/v2/internal/sysutil\"\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n)\n\nfunc (s *service) DebugListGroups(_ *protocoltypes.DebugListGroups_Request, srv protocoltypes.ProtocolService_DebugListGroupsServer) error {\n\taccountGroup := s.getAccountGroup()\n\tif accountGroup == nil {\n\t\treturn errcode.ErrCode_ErrGroupMissing\n\t}\n\n\tif err := srv.SendMsg(&protocoltypes.DebugListGroups_Reply{\n\t\tGroupPk:   accountGroup.group.PublicKey,\n\t\tGroupType: accountGroup.group.GroupType,\n\t}); err != nil {\n\t\treturn err\n\t}\n\n\tfor _, c := range accountGroup.MetadataStore().ListContactsByStatus(protocoltypes.ContactState_ContactStateAdded) {\n\t\tpk, err := crypto.UnmarshalEd25519PublicKey(c.Pk)\n\t\tif err != nil {\n\t\t\treturn errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t\t}\n\n\t\tgroup, err := s.secretStore.GetGroupForContact(pk)\n\t\tif err != nil {\n\t\t\treturn errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err)\n\t\t}\n\n\t\tif err := srv.SendMsg(&protocoltypes.DebugListGroups_Reply{\n\t\t\tGroupPk:   group.PublicKey,\n\t\t\tGroupType: group.GroupType,\n\t\t\tContactPk: c.Pk,\n\t\t}); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tfor _, g := range accountGroup.MetadataStore().ListMultiMemberGroups() {\n\t\tif err := srv.SendMsg(&protocoltypes.DebugListGroups_Reply{\n\t\t\tGroupPk:   g.PublicKey,\n\t\t\tGroupType: g.GroupType,\n\t\t}); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (s *service) DebugInspectGroupStore(req *protocoltypes.DebugInspectGroupStore_Request, srv protocoltypes.ProtocolService_DebugInspectGroupStoreServer) error {\n\tif req.LogType == protocoltypes.DebugInspectGroupLogType_DebugInspectGroupLogTypeUndefined {\n\t\treturn errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"invalid log type specified\"))\n\t}\n\n\tcg, err := s.GetContextGroupForID(req.GroupPk)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrInvalidInput.Wrap(err)\n\t}\n\n\tswitch req.LogType {\n\tcase protocoltypes.DebugInspectGroupLogType_DebugInspectGroupLogTypeMessage:\n\t\tfor _, e := range cg.messageStore.OpLog().GetEntries().Slice() {\n\t\t\tvar (\n\t\t\t\tpayload  = []byte(nil)\n\t\t\t\tdevicePK = []byte(nil)\n\t\t\t\tnexts    = make([][]byte, len(e.GetNext()))\n\t\t\t)\n\n\t\t\tif evt, err := cg.messageStore.openMessage(srv.Context(), e); err != nil {\n\t\t\t\ts.logger.Error(\"unable to open message\", zap.Error(err))\n\t\t\t} else {\n\t\t\t\tdevicePK = evt.Headers.DevicePk\n\t\t\t\tpayload = evt.Message\n\t\t\t}\n\n\t\t\tfor i, n := range e.GetNext() {\n\t\t\t\tnexts[i] = n.Bytes()\n\t\t\t}\n\n\t\t\tif err := srv.SendMsg(&protocoltypes.DebugInspectGroupStore_Reply{\n\t\t\t\tCid:        e.GetHash().Bytes(),\n\t\t\t\tParentCids: nexts,\n\t\t\t\tDevicePk:   devicePK,\n\t\t\t\tPayload:    payload,\n\t\t\t}); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\tcase protocoltypes.DebugInspectGroupLogType_DebugInspectGroupLogTypeMetadata:\n\t\tlog := cg.metadataStore.OpLog()\n\n\t\tfor _, e := range log.GetEntries().Slice() {\n\t\t\tvar (\n\t\t\t\teventType protocoltypes.EventType\n\t\t\t\tpayload   = []byte(nil)\n\t\t\t\tdevicePK  = []byte(nil)\n\t\t\t\tnexts     = make([][]byte, len(e.GetNext()))\n\t\t\t)\n\n\t\t\tif op, err := operation.ParseOperation(e); err != nil {\n\t\t\t\ts.logger.Error(\"unable to parse operation\", zap.Error(err))\n\t\t\t} else if meta, event, err := openGroupEnvelope(cg.group, op.GetValue()); err != nil {\n\t\t\t\ts.logger.Error(\"unable to open group envelope\", zap.Error(err))\n\t\t\t} else if metaEvent, err := newGroupMetadataEventFromEntry(log, e, meta, event, cg.group); err != nil {\n\t\t\t\ts.logger.Error(\"unable to get group metadata event from entry\", zap.Error(err))\n\t\t\t} else {\n\t\t\t\tpayload = metaEvent.Event\n\t\t\t\teventType = metaEvent.Metadata.EventType\n\n\t\t\t\tif typeData, ok := eventTypesMapper[metaEvent.Metadata.EventType]; ok {\n\t\t\t\t\tp := proto.Clone(typeData.Message)\n\t\t\t\t\tif err := proto.Unmarshal(metaEvent.Event, p); err == nil {\n\t\t\t\t\t\tif msg, ok := p.(eventDeviceSigned); ok {\n\t\t\t\t\t\t\tdevicePK = msg.GetDevicePk()\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\ts.logger.Error(\"unable to get message struct for event type\", zap.String(\"event_type\", metaEvent.Metadata.EventType.String()))\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor i, n := range e.GetNext() {\n\t\t\t\tnexts[i] = n.Bytes()\n\t\t\t}\n\n\t\t\tif err := srv.SendMsg(&protocoltypes.DebugInspectGroupStore_Reply{\n\t\t\t\tCid:               e.GetHash().Bytes(),\n\t\t\t\tParentCids:        nexts,\n\t\t\t\tPayload:           payload,\n\t\t\t\tMetadataEventType: eventType,\n\t\t\t\tDevicePk:          devicePK,\n\t\t\t}); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (s *service) DebugGroup(ctx context.Context, request *protocoltypes.DebugGroup_Request) (*protocoltypes.DebugGroup_Reply, error) {\n\trep := &protocoltypes.DebugGroup_Reply{}\n\n\tpeers, err := s.ipfsCoreAPI.Swarm().Peers(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\ttopic := fmt.Sprintf(\"grp_%s\", string(request.GroupPk))\n\n\tfor _, p := range peers {\n\t\ttagInfo := s.ipfsCoreAPI.ConnMgr().GetTagInfo(p.ID())\n\t\tif _, ok := tagInfo.Tags[topic]; ok {\n\t\t\trep.PeerIds = append(rep.PeerIds, p.ID().String())\n\t\t}\n\t}\n\n\treturn rep, nil\n}\n\nfunc (s *service) SystemInfo(ctx context.Context, _ *protocoltypes.SystemInfo_Request) (*protocoltypes.SystemInfo_Reply, error) {\n\treply := protocoltypes.SystemInfo_Reply{}\n\n\t// process\n\tprocess, errs := sysutil.SystemInfoProcess()\n\treply.Process = process\n\treply.Process.StartedAt = s.startedAt.Unix()\n\treply.Process.UptimeMs = time.Since(s.startedAt).Milliseconds()\n\n\t// gRPC\n\t// TODO\n\n\t// p2p\n\t{\n\t\treply.P2P = &protocoltypes.SystemInfo_P2P{}\n\n\t\t// swarm metrics\n\t\tif api := s.IpfsCoreAPI(); api != nil {\n\t\t\tpeers, err := api.Swarm().Peers(ctx)\n\t\t\treply.P2P.ConnectedPeers = int64(len(peers))\n\t\t\terrs = multierr.Append(errs, err)\n\t\t} else {\n\t\t\terrs = multierr.Append(errs, fmt.Errorf(\"no such IPFS core API\"))\n\t\t}\n\n\t\t// pubsub metrics\n\t\t// TODO\n\n\t\t// BLE metrics\n\t}\n\n\t// OrbitDB\n\taccountGroup := s.getAccountGroup()\n\tif accountGroup == nil {\n\t\treturn nil, errcode.ErrCode_ErrGroupMissing\n\t}\n\tstatus := accountGroup.metadataStore.ReplicationStatus()\n\treply.Orbitdb = &protocoltypes.SystemInfo_OrbitDB{\n\t\tAccountMetadata: &protocoltypes.SystemInfo_OrbitDB_ReplicationStatus{\n\t\t\tProgress: int64(status.GetProgress()),\n\t\t\tMaximum:  int64(status.GetMax()),\n\t\t},\n\t}\n\t// FIXME: compute more stores\n\n\t// warns\n\tif errs != nil {\n\t\treply.Warns = []string{}\n\t\tfor _, err := range multierr.Errors(errs) {\n\t\t\treply.Warns = append(reply.Warns, err.Error())\n\t\t}\n\t}\n\n\treturn &reply, nil\n}\n\nfunc (s *service) PeerList(ctx context.Context, _ *protocoltypes.PeerList_Request) (*protocoltypes.PeerList_Reply, error) {\n\treply := protocoltypes.PeerList_Reply{}\n\tapi := s.IpfsCoreAPI()\n\tif api == nil {\n\t\treturn nil, errcode.ErrCode_TODO.Wrap(fmt.Errorf(\"IPFS Core API is not available\"))\n\t}\n\tswarmPeers, err := api.Swarm().Peers(ctx) // https://pkg.go.dev/github.com/ipfs/interface-go-ipfs-core#ConnectionInfo\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_TODO.Wrap(err)\n\t}\n\n\tpeers := map[peer.ID]*protocoltypes.PeerList_Peer{}\n\n\t// each peer in the swarm should be visible\n\tfor _, swarmPeer := range swarmPeers {\n\t\tpeers[swarmPeer.ID()] = &protocoltypes.PeerList_Peer{\n\t\t\tId:     swarmPeer.ID().String(),\n\t\t\tErrors: []string{},\n\t\t\tRoutes: []*protocoltypes.PeerList_Route{},\n\t\t}\n\t}\n\t// FIXME: do not restrict on swarm peers, also print some other important ones (old, etc)\n\n\t// append peer addrs from peerstore\n\tfor peerID, peer := range peers {\n\t\tinfo := s.host.Peerstore().PeerInfo(peerID)\n\t\tfor _, addr := range info.Addrs {\n\t\t\tpeer.Routes = append(peer.Routes, &protocoltypes.PeerList_Route{\n\t\t\t\tAddress: addr.String(),\n\t\t\t})\n\t\t}\n\t}\n\n\t// append more info for active connections\n\tfor _, swarmPeer := range swarmPeers {\n\t\tpeer, ok := peers[swarmPeer.ID()]\n\t\tif !ok {\n\t\t\tpeer = &protocoltypes.PeerList_Peer{\n\t\t\t\tId:     swarmPeer.ID().String(),\n\t\t\t\tErrors: []string{},\n\t\t\t\tRoutes: []*protocoltypes.PeerList_Route{},\n\t\t\t}\n\t\t\tpeer.Errors = append(peer.Errors, \"peer in swarm peers, but not in peerstore\")\n\t\t\tpeers[swarmPeer.ID()] = peer\n\t\t}\n\n\t\taddress := swarmPeer.Address().String()\n\t\tfound := false\n\t\tvar selectedRoute *protocoltypes.PeerList_Route\n\t\tfor _, route := range peer.Routes {\n\t\t\tif route.Address == address {\n\t\t\t\tfound = true\n\t\t\t\tselectedRoute = route\n\t\t\t}\n\t\t}\n\t\tif !found {\n\t\t\tnewRoute := protocoltypes.PeerList_Route{Address: address}\n\t\t\tpeer.Routes = append(peer.Routes, &newRoute)\n\t\t\tselectedRoute = &newRoute\n\t\t}\n\t\tselectedRoute.IsActive = true\n\t\t// latency\n\t\t{\n\t\t\tlatency, err := swarmPeer.Latency()\n\t\t\tif err != nil {\n\t\t\t\tpeer.Errors = append(peer.Errors, err.Error())\n\t\t\t} else {\n\t\t\t\tselectedRoute.Latency = latency.Milliseconds()\n\t\t\t}\n\t\t}\n\t\t// direction\n\t\t{\n\t\t\tswitch swarmPeer.Direction() {\n\t\t\tcase network.DirInbound:\n\t\t\t\tselectedRoute.Direction = protocoltypes.Direction_InboundDir\n\t\t\tcase network.DirOutbound:\n\t\t\t\tselectedRoute.Direction = protocoltypes.Direction_OutboundDir\n\t\t\t}\n\t\t}\n\t\t// streams\n\t\t{\n\t\t\tpeerStreams, err := swarmPeer.Streams()\n\t\t\tif err != nil {\n\t\t\t\tpeer.Errors = append(peer.Errors, err.Error())\n\t\t\t} else {\n\t\t\t\tselectedRoute.Streams = []*protocoltypes.PeerList_Stream{}\n\t\t\t\tfor _, peerStream := range peerStreams {\n\t\t\t\t\tif peerStream == \"\" {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tselectedRoute.Streams = append(selectedRoute.Streams, &protocoltypes.PeerList_Stream{\n\t\t\t\t\t\tId: string(peerStream),\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// compute features\n\tfor _, peer := range peers {\n\t\tfeatures := map[protocoltypes.PeerList_Feature]bool{}\n\t\tfor _, route := range peer.Routes {\n\t\t\t// FIXME: use the multiaddr library instead of string comparisons\n\t\t\tif strings.Contains(route.Address, \"/quic\") {\n\t\t\t\tfeatures[protocoltypes.PeerList_QuicFeature] = true\n\t\t\t}\n\t\t\tif strings.Contains(route.Address, \"/mc/\") {\n\t\t\t\tfeatures[protocoltypes.PeerList_BLEFeature] = true\n\t\t\t\tfeatures[protocoltypes.PeerList_WeshFeature] = true\n\t\t\t}\n\t\t\tif strings.Contains(route.Address, \"/tor/\") {\n\t\t\t\tfeatures[protocoltypes.PeerList_TorFeature] = true\n\t\t\t}\n\t\t\tfor _, stream := range route.Streams {\n\t\t\t\tif stream.Id == \"/wesh/contact_req/1.0.0\" {\n\t\t\t\t\tfeatures[protocoltypes.PeerList_WeshFeature] = true\n\t\t\t\t}\n\t\t\t\tif stream.Id == \"/rendezvous/1.0.0\" {\n\t\t\t\t\tfeatures[protocoltypes.PeerList_WeshFeature] = true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfor feature := range features {\n\t\t\tpeer.Features = append(peer.Features, feature)\n\t\t}\n\t}\n\n\t// compute peer-level aggregates\n\tfor _, peer := range peers {\n\t\t// aggregate direction\n\t\tfor _, route := range peer.Routes {\n\t\t\tif route.Direction == protocoltypes.Direction_UnknownDir {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tswitch {\n\t\t\tcase peer.Direction == protocoltypes.Direction_UnknownDir: // first route with a direction\n\t\t\t\tpeer.Direction = route.Direction\n\t\t\tcase peer.Direction == protocoltypes.Direction_BiDir: // peer aggregate is already maximal\n\t\t\t\t// noop\n\t\t\tcase route.Direction == peer.Direction: // another route with the same direction\n\t\t\t\t// noop\n\t\t\tcase route.Direction == protocoltypes.Direction_InboundDir && peer.Direction == protocoltypes.Direction_OutboundDir:\n\t\t\t\tpeer.Direction = protocoltypes.Direction_BiDir\n\t\t\tcase route.Direction == protocoltypes.Direction_OutboundDir && peer.Direction == protocoltypes.Direction_InboundDir:\n\t\t\t\tpeer.Direction = protocoltypes.Direction_BiDir\n\t\t\tdefault:\n\t\t\t\tpeer.Errors = append(peer.Errors, \"failed to compute direction aggregate\")\n\t\t\t}\n\t\t}\n\n\t\t// aggregate latency\n\t\tfor _, route := range peer.Routes {\n\t\t\tif route.Latency == 0 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tswitch {\n\t\t\tcase peer.MinLatency == 0: // first route with a latency\n\t\t\t\tpeer.MinLatency = route.Latency\n\t\t\tcase peer.MinLatency > route.Latency: // smaller value\n\t\t\t\tpeer.MinLatency = route.Latency\n\t\t\t}\n\t\t}\n\n\t\t// aggregate isActive\n\t\tfor _, route := range peer.Routes {\n\t\t\tif route.IsActive {\n\t\t\t\tpeer.IsActive = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\n\t// FIXME: compute pubsub peers too?\n\n\t// FIXME: add metrics about \"amount of times seen\", \"first time seen\", \"bandwidth\"\n\n\t// use protobuf format\n\tfor _, peer := range peers {\n\t\treply.Peers = append(reply.Peers, peer)\n\t}\n\treturn &reply, nil\n}\n"
  },
  {
    "path": "api_event.go",
    "content": "package weshnet\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/libp2p/go-libp2p/p2p/host/eventbus\"\n\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n)\n\nfunc checkParametersConsistency(sinceID, untilID []byte, sinceNow, untilNow, reverseOrder bool) error {\n\t// Since can't be both set to an ID and to now\n\tif sinceID != nil && sinceNow {\n\t\treturn errcode.ErrCode_ErrInvalidInput.Wrap(errors.New(\"params SinceNow and SinceID are both set\"))\n\t}\n\t// Until can't be both set to an ID and to now\n\tif untilID != nil && untilNow {\n\t\treturn errcode.ErrCode_ErrInvalidInput.Wrap(errors.New(\"params UntilNow and UntilID are both set\"))\n\t}\n\t// Since and Until can't be both set to now at the same time\n\tif sinceNow && untilNow {\n\t\treturn errcode.ErrCode_ErrInvalidInput.Wrap(errors.New(\"params SinceNow and UntilNow are both set\"))\n\t}\n\t// Can't reverse events orders if subscribed to new events\n\tif untilID == nil && !untilNow && reverseOrder {\n\t\treturn errcode.ErrCode_ErrInvalidInput.Wrap(errors.New(\"reverse chronological order requested while subscribing to new events\"))\n\t}\n\n\treturn nil\n}\n\n// GroupMetadataList replays previous and subscribes to new metadata events from the group\nfunc (s *service) GroupMetadataList(req *protocoltypes.GroupMetadataList_Request, sub protocoltypes.ProtocolService_GroupMetadataListServer) error {\n\tctx, cancel := context.WithCancel(sub.Context())\n\tdefer cancel()\n\n\t// Get group context / check if the group is opened\n\tcg, err := s.GetContextGroupForID(req.GroupPk)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrGroupMemberUnknownGroupID.Wrap(err)\n\t}\n\n\t// Check parameters consistency\n\tif err := checkParametersConsistency(req.SinceId, req.UntilId, req.SinceNow, req.UntilNow, req.ReverseOrder); err != nil {\n\t\treturn err\n\t}\n\n\t// Subscribe to new metadata events if requested\n\tvar newEvents <-chan any\n\tif req.UntilId == nil && !req.UntilNow {\n\t\tsub, err := cg.MetadataStore().EventBus().Subscribe([]any{\n\t\t\t// new(stores.EventReplicated),\n\t\t\tnew(*protocoltypes.GroupMetadataEvent),\n\t\t}, eventbus.Name(\"weshnet/api/group-metadata-list\"), eventbus.BufSize(32))\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"unable to subscribe to new events\")\n\t\t}\n\t\tdefer sub.Close()\n\t\tnewEvents = sub.Out()\n\t}\n\n\t// Subscribe to previous metadata events and stream them if requested\n\tpreviousEvents := make(chan *protocoltypes.GroupMetadataEvent)\n\tif !req.SinceNow {\n\t\tpevt, err := cg.MetadataStore().ListEvents(ctx, req.SinceId, req.UntilId, req.ReverseOrder)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tgo func() {\n\t\t\tfor {\n\t\t\t\tvar evt *protocoltypes.GroupMetadataEvent\n\t\t\t\tselect {\n\t\t\t\tcase <-ctx.Done():\n\t\t\t\t\treturn\n\t\t\t\tcase evt = <-pevt:\n\t\t\t\t}\n\n\t\t\t\tif evt == nil {\n\t\t\t\t\t// if we don't want to stream new event, cancel the process\n\t\t\t\t\tif req.UntilNow {\n\t\t\t\t\t\tcancel()\n\t\t\t\t\t} else {\n\t\t\t\t\t\tpreviousEvents <- &protocoltypes.GroupMetadataEvent{EventContext: nil}\n\t\t\t\t\t}\n\n\t\t\t\t\tcg.logger.Debug(\"GroupMetadataList: previous events stream ended\")\n\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tpreviousEvents <- evt\n\t\t\t}\n\t\t}()\n\t}\n\n\t// Subscribe to new metadata events and stream them if requested\n\tfor {\n\t\tvar event any\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\treturn nil\n\t\tcase event = <-previousEvents:\n\t\tcase event = <-newEvents:\n\t\t}\n\n\t\tmsg := event.(*protocoltypes.GroupMetadataEvent)\n\t\tif msg.EventContext == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tif err := sub.Send(msg); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tcg.logger.Info(\"service - metadata store - sent 1 event from log subscription\")\n\t}\n}\n\n// GroupMessageList replays previous and subscribes to new message events from the group\nfunc (s *service) GroupMessageList(req *protocoltypes.GroupMessageList_Request, sub protocoltypes.ProtocolService_GroupMessageListServer) error {\n\tctx, cancel := context.WithCancel(sub.Context())\n\tdefer cancel()\n\n\t// Get group context / check if the group is opened\n\tcg, err := s.GetContextGroupForID(req.GroupPk)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrGroupMemberUnknownGroupID.Wrap(err)\n\t}\n\n\t// Check parameters consistency\n\tif err := checkParametersConsistency(req.SinceId, req.UntilId, req.SinceNow, req.UntilNow, req.ReverseOrder); err != nil {\n\t\treturn err\n\t}\n\n\t// Subscribe to new message events if requested\n\tvar newEvents <-chan any\n\tif req.UntilId == nil && !req.UntilNow {\n\t\tmessageStoreSub, err := cg.MessageStore().EventBus().Subscribe([]any{\n\t\t\tnew(*protocoltypes.GroupMessageEvent),\n\t\t}, eventbus.Name(\"weshnet/api/group-message-list\"))\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"unable to subscribe to new events\")\n\t\t}\n\t\tdefer messageStoreSub.Close()\n\t\tnewEvents = messageStoreSub.Out()\n\t}\n\n\t// Subscribe to previous message events and stream them if requested\n\tpreviousEvents := make(chan *protocoltypes.GroupMessageEvent)\n\tif !req.SinceNow {\n\t\tpevt, err := cg.MessageStore().ListEvents(ctx, req.SinceId, req.UntilId, req.ReverseOrder)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tgo func() {\n\t\t\tfor {\n\t\t\t\tvar evt *protocoltypes.GroupMessageEvent\n\t\t\t\tselect {\n\t\t\t\tcase <-ctx.Done():\n\t\t\t\t\treturn\n\t\t\t\tcase evt = <-pevt:\n\t\t\t\t}\n\n\t\t\t\tif evt == nil {\n\t\t\t\t\t// if we don't want to stream new event, cancel the process\n\t\t\t\t\tif req.UntilNow {\n\t\t\t\t\t\tcancel()\n\t\t\t\t\t} else {\n\t\t\t\t\t\tpreviousEvents <- &protocoltypes.GroupMessageEvent{EventContext: nil}\n\t\t\t\t\t}\n\n\t\t\t\t\tcg.logger.Debug(\"GroupMessageList: previous events stream ended\")\n\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tpreviousEvents <- evt\n\t\t\t}\n\t\t}()\n\t}\n\n\t// Subscribe to new message events and stream them if requested\n\tfor {\n\t\tvar event any\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\treturn nil\n\t\tcase event = <-previousEvents:\n\t\tcase event = <-newEvents:\n\t\t}\n\n\t\tmsg := event.(*protocoltypes.GroupMessageEvent)\n\t\tif msg.EventContext == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tif err := sub.Send(msg); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tcg.logger.Info(\"service - message store - sent 1 event from log subscription\")\n\t}\n}\n"
  },
  {
    "path": "api_group.go",
    "content": "package weshnet\n\nimport (\n\t\"context\"\n\t\"encoding/base64\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\n\t\"github.com/libp2p/go-libp2p/core/crypto\"\n\tpeer \"github.com/libp2p/go-libp2p/core/peer\"\n\tmanet \"github.com/multiformats/go-multiaddr/net\"\n\t\"go.uber.org/zap\"\n\t\"google.golang.org/protobuf/proto\"\n\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n\t\"berty.tech/weshnet/v2/pkg/logutil\"\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n)\n\nfunc (s *service) GroupInfo(ctx context.Context, req *protocoltypes.GroupInfo_Request) (*protocoltypes.GroupInfo_Reply, error) {\n\tvar (\n\t\tg   *protocoltypes.Group\n\t\terr error\n\t)\n\n\tswitch {\n\tcase req.GroupPk != nil:\n\t\tpk, err := crypto.UnmarshalEd25519PublicKey(req.GroupPk)\n\t\tif err != nil {\n\t\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(err)\n\t\t}\n\n\t\tg, err = s.getGroupForPK(ctx, pk)\n\t\tif err != nil {\n\t\t\treturn nil, errcode.ErrCode_TODO.Wrap(err)\n\t\t}\n\tcase req.ContactPk != nil:\n\t\tpk, err := crypto.UnmarshalEd25519PublicKey(req.ContactPk)\n\t\tif err != nil {\n\t\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(err)\n\t\t}\n\n\t\tg, err = s.getContactGroup(pk)\n\t\tif err != nil {\n\t\t\treturn nil, errcode.ErrCode_ErrOrbitDBOpen.Wrap(err)\n\t\t}\n\tdefault:\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput\n\t}\n\n\tmemberDevice, err := s.secretStore.GetOwnMemberDeviceForGroup(g)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_TODO.Wrap(err)\n\t}\n\n\tmember, err := memberDevice.Member().Raw()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\tdevice, err := memberDevice.Device().Raw()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\treturn &protocoltypes.GroupInfo_Reply{\n\t\tGroup:    g,\n\t\tMemberPk: member,\n\t\tDevicePk: device,\n\t}, nil\n}\n\nfunc (s *service) ActivateGroup(ctx context.Context, req *protocoltypes.ActivateGroup_Request) (*protocoltypes.ActivateGroup_Reply, error) {\n\tpk, err := crypto.UnmarshalEd25519PublicKey(req.GroupPk)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(err)\n\t}\n\n\terr = s.activateGroup(ctx, pk, req.LocalOnly)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\n\treturn &protocoltypes.ActivateGroup_Reply{}, nil\n}\n\nfunc (s *service) DeactivateGroup(_ context.Context, req *protocoltypes.DeactivateGroup_Request) (*protocoltypes.DeactivateGroup_Reply, error) {\n\tpk, err := crypto.UnmarshalEd25519PublicKey(req.GroupPk)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(err)\n\t}\n\n\tif err := s.deactivateGroup(pk); err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\n\treturn &protocoltypes.DeactivateGroup_Reply{}, nil\n}\n\nfunc (s *service) GroupDeviceStatus(req *protocoltypes.GroupDeviceStatus_Request, srv protocoltypes.ProtocolService_GroupDeviceStatusServer) error {\n\tctx := srv.Context()\n\tgkey := hex.EncodeToString(req.GroupPk)\n\tpeers := PeersConnectedness{}\n\n\tlogger := s.logger.Named(\"pstatus\")\n\tlogger.Debug(\"start monitor device status group\", logutil.PrivateString(\"group_key\", gkey))\n\n\tfor {\n\t\tupdated, ok := s.peerStatusManager.WaitForConnectednessChange(ctx, gkey, peers)\n\t\tif !ok {\n\t\t\treturn nil // server context has expired\n\t\t}\n\n\t\t// send updated peers\n\t\tvar err error\n\t\tfor _, peer := range updated {\n\t\t\tvar evt protocoltypes.GroupDeviceStatus_Reply\n\n\t\t\tswitch peers[peer] {\n\t\t\tcase ConnectednessTypeConnected:\n\t\t\t\tevt.Type = protocoltypes.GroupDeviceStatus_TypePeerConnected\n\t\t\t\tvar connected *protocoltypes.GroupDeviceStatus_Reply_PeerConnected\n\t\t\t\tif connected, err = s.craftPeerConnectedMessage(peer); err == nil {\n\t\t\t\t\tevt.Event, err = proto.Marshal(connected)\n\t\t\t\t\tlogger.Debug(\"peer connected\",\n\t\t\t\t\t\tlogutil.PrivateString(\"group_key\", gkey),\n\t\t\t\t\t\tlogutil.PrivateString(\"peer\", connected.PeerId),\n\t\t\t\t\t\tlogutil.PrivateString(\"devicePK\", base64.URLEncoding.EncodeToString(connected.GetDevicePk())))\n\t\t\t\t}\n\n\t\t\tcase ConnectednessTypeDisconnected:\n\t\t\t\tevt.Type = protocoltypes.GroupDeviceStatus_TypePeerDisconnected\n\t\t\t\tdisconnected := s.craftDeviceDisconnectedMessage(peer)\n\t\t\t\tlogger.Debug(\"peer disconnected\",\n\t\t\t\t\tlogutil.PrivateString(\"group_key\", gkey),\n\t\t\t\t\tlogutil.PrivateString(\"peer\", disconnected.PeerId))\n\n\t\t\t\tevt.Event, err = proto.Marshal(disconnected)\n\t\t\tcase ConnectednessTypeReconnecting:\n\t\t\t\tevt.Type = protocoltypes.GroupDeviceStatus_TypePeerConnected\n\t\t\t\treconnecting := s.craftDeviceReconnectedMessage(peer)\n\t\t\t\tlogger.Debug(\"peer reconnecting\",\n\t\t\t\t\tlogutil.PrivateString(\"group_key\", gkey),\n\t\t\t\t\tlogutil.PrivateString(\"peer\", reconnecting.PeerId))\n\t\t\t\tevt.Event, err = proto.Marshal(reconnecting)\n\n\t\t\tdefault:\n\t\t\t\tevt.Type = protocoltypes.GroupDeviceStatus_TypeUnknown\n\t\t\t}\n\n\t\t\tif err != nil {\n\t\t\t\tlogger.Error(\"GroupDeviceStatus: unable to handle event\", zap.Error(err))\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif err := srv.Send(&evt); err != nil {\n\t\t\t\tlogger.Debug(\"GroupDeviceStatus: failed to send event\", zap.Error(err))\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc (s *service) craftPeerConnectedMessage(peer peer.ID) (*protocoltypes.GroupDeviceStatus_Reply_PeerConnected, error) {\n\tpdg, ok := s.odb.GetDevicePKForPeerID(peer)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"PeerDeviceGroup unknown\")\n\t}\n\n\tdevicePKRaw, err := pdg.DevicePK.Raw()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to get raw devicePK: %w\", err)\n\t}\n\n\tconnected := protocoltypes.GroupDeviceStatus_Reply_PeerConnected{\n\t\tPeerId:   peer.String(),\n\t\tDevicePk: devicePKRaw,\n\t}\n\n\tactiveConns := s.host.Network().ConnsToPeer(peer)\n\tconnected.Transports = make([]protocoltypes.GroupDeviceStatus_Transport, len(activeConns))\n\tconnected.Maddrs = make([]string, len(activeConns))\n\nCONN_LOOP:\n\tfor i, conn := range activeConns {\n\t\tconnected.Maddrs[i] = conn.RemoteMultiaddr().String()\n\n\t\t// check for proximity transport\n\t\tprotocols := conn.RemoteMultiaddr().Protocols()\n\t\tfor _, protocol := range protocols {\n\t\t\tswitch protocol.Name {\n\t\t\tcase \"nearby\", \"mc\", \"ble\":\n\t\t\t\tconnected.Transports[i] = protocoltypes.GroupDeviceStatus_TptProximity\n\t\t\t\tcontinue CONN_LOOP\n\t\t\t}\n\t\t}\n\n\t\t// otherwise, check for WAN/LAN addr\n\t\tif manet.IsPrivateAddr(conn.RemoteMultiaddr()) {\n\t\t\tconnected.Transports[i] = protocoltypes.GroupDeviceStatus_TptLAN\n\t\t} else {\n\t\t\tconnected.Transports[i] = protocoltypes.GroupDeviceStatus_TptWAN\n\t\t}\n\t}\n\n\treturn &connected, nil\n}\n\nfunc (s *service) craftDeviceDisconnectedMessage(peer peer.ID) *protocoltypes.GroupDeviceStatus_Reply_PeerDisconnected {\n\treturn &protocoltypes.GroupDeviceStatus_Reply_PeerDisconnected{\n\t\tPeerId: peer.String(),\n\t}\n}\n\nfunc (s *service) craftDeviceReconnectedMessage(peer peer.ID) *protocoltypes.GroupDeviceStatus_Reply_PeerReconnecting {\n\treturn &protocoltypes.GroupDeviceStatus_Reply_PeerReconnecting{\n\t\tPeerId: peer.String(),\n\t}\n}\n"
  },
  {
    "path": "api_multimember.go",
    "content": "package weshnet\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/libp2p/go-libp2p/core/crypto\"\n\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n\t\"berty.tech/weshnet/v2/pkg/tyber\"\n)\n\n// MultiMemberGroupCreate creates a new MultiMember group\nfunc (s *service) MultiMemberGroupCreate(ctx context.Context, _ *protocoltypes.MultiMemberGroupCreate_Request) (_ *protocoltypes.MultiMemberGroupCreate_Reply, err error) {\n\tctx, _, endSection := tyber.Section(ctx, s.logger, \"Creating MultiMember group\")\n\tdefer func() { endSection(err, \"\") }()\n\n\tgroup, groupPrivateKey, err := NewGroupMultiMember()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err)\n\t}\n\n\taccountGroup := s.getAccountGroup()\n\tif accountGroup == nil {\n\t\treturn nil, errcode.ErrCode_ErrGroupMissing\n\t}\n\n\t_, err = accountGroup.MetadataStore().GroupJoin(ctx, group)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrOrbitDBAppend.Wrap(err)\n\t}\n\n\tif err := s.secretStore.PutGroup(ctx, group); err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\n\terr = s.activateGroup(ctx, groupPrivateKey.GetPublic(), false)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInternal.Wrap(fmt.Errorf(\"unable to activate group: %w\", err))\n\t}\n\n\tcg, err := s.GetContextGroupForID(group.PublicKey)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrOrbitDBAppend.Wrap(err)\n\t}\n\n\t_, err = cg.MetadataStore().ClaimGroupOwnership(ctx, groupPrivateKey)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrOrbitDBAppend.Wrap(err)\n\t}\n\n\treturn &protocoltypes.MultiMemberGroupCreate_Reply{\n\t\tGroupPk: group.PublicKey,\n\t}, nil\n}\n\n// MultiMemberGroupJoin joins an existing MultiMember group using an invitation\nfunc (s *service) MultiMemberGroupJoin(ctx context.Context, req *protocoltypes.MultiMemberGroupJoin_Request) (_ *protocoltypes.MultiMemberGroupJoin_Reply, err error) {\n\tctx, _, endSection := tyber.Section(ctx, s.logger, \"Joining MultiMember group\")\n\tdefer func() { endSection(err, \"\") }()\n\n\taccountGroup := s.getAccountGroup()\n\tif accountGroup == nil {\n\t\treturn nil, errcode.ErrCode_ErrGroupMissing\n\t}\n\n\tif _, err := accountGroup.MetadataStore().GroupJoin(ctx, req.Group); err != nil {\n\t\treturn nil, errcode.ErrCode_ErrOrbitDBAppend.Wrap(err)\n\t}\n\n\treturn &protocoltypes.MultiMemberGroupJoin_Reply{}, nil\n}\n\n// MultiMemberGroupLeave leaves a previously joined MultiMember group\nfunc (s *service) MultiMemberGroupLeave(ctx context.Context, req *protocoltypes.MultiMemberGroupLeave_Request) (_ *protocoltypes.MultiMemberGroupLeave_Reply, err error) {\n\tctx, _, endSection := tyber.Section(ctx, s.logger, \"Leaving MultiMember group\")\n\tdefer func() { endSection(err, \"\") }()\n\n\tpk, err := crypto.UnmarshalEd25519PublicKey(req.GroupPk)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\taccountGroup := s.getAccountGroup()\n\tif accountGroup == nil {\n\t\treturn nil, errcode.ErrCode_ErrGroupMissing\n\t}\n\n\t_, err = accountGroup.MetadataStore().GroupLeave(ctx, pk)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrOrbitDBAppend.Wrap(err)\n\t}\n\n\tif err := s.deactivateGroup(pk); err != nil {\n\t\treturn nil, errcode.ErrCode_ErrOrbitDBAppend.Wrap(err)\n\t}\n\n\treturn &protocoltypes.MultiMemberGroupLeave_Reply{}, nil\n}\n\n// MultiMemberGroupAliasResolverDisclose sends an deviceKeystore identity proof to the group members\nfunc (s *service) MultiMemberGroupAliasResolverDisclose(ctx context.Context, req *protocoltypes.MultiMemberGroupAliasResolverDisclose_Request) (*protocoltypes.MultiMemberGroupAliasResolverDisclose_Reply, error) {\n\tcg, err := s.GetContextGroupForID(req.GroupPk)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrGroupMemberUnknownGroupID.Wrap(err)\n\t}\n\n\t_, err = cg.MetadataStore().SendAliasProof(ctx)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrOrbitDBAppend.Wrap(err)\n\t}\n\n\treturn &protocoltypes.MultiMemberGroupAliasResolverDisclose_Reply{}, nil\n}\n\n// MultiMemberGroupAdminRoleGrant grants admin role to another member of the group\nfunc (s *service) MultiMemberGroupAdminRoleGrant(context.Context, *protocoltypes.MultiMemberGroupAdminRoleGrant_Request) (*protocoltypes.MultiMemberGroupAdminRoleGrant_Reply, error) {\n\treturn nil, errcode.ErrCode_ErrNotImplemented\n}\n\n// MultiMemberGroupInvitationCreate creates a group invitation\nfunc (s *service) MultiMemberGroupInvitationCreate(_ context.Context, req *protocoltypes.MultiMemberGroupInvitationCreate_Request) (*protocoltypes.MultiMemberGroupInvitationCreate_Reply, error) {\n\tcg, err := s.GetContextGroupForID(req.GroupPk)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrGroupMemberUnknownGroupID.Wrap(err)\n\t}\n\n\treturn &protocoltypes.MultiMemberGroupInvitationCreate_Reply{\n\t\tGroup: cg.Group(),\n\t}, nil\n}\n"
  },
  {
    "path": "api_replication.go",
    "content": "package weshnet\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\n\t\"go.uber.org/zap\"\n\t\"google.golang.org/grpc\"\n\t\"google.golang.org/grpc/credentials\"\n\t\"google.golang.org/grpc/credentials/insecure\"\n\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n\t\"berty.tech/weshnet/v2/pkg/grpcutil\"\n\t\"berty.tech/weshnet/v2/pkg/logutil\"\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n\t\"berty.tech/weshnet/v2/pkg/replicationtypes\"\n\t\"berty.tech/weshnet/v2/pkg/tyber\"\n)\n\nfunc FilterGroupForReplication(m *protocoltypes.Group) (*protocoltypes.Group, error) {\n\tgroupSigPK, err := m.GetSigningPubKey()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_TODO.Wrap(err)\n\t}\n\n\tgroupSigPKBytes, err := groupSigPK.Raw()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\tlinkKey, err := m.GetLinkKeyArray()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_TODO.Wrap(err)\n\t}\n\n\treturn &protocoltypes.Group{\n\t\tPublicKey:  m.PublicKey,\n\t\tSignPub:    groupSigPKBytes,\n\t\tLinkKey:    linkKey[:],\n\t\tLinkKeySig: m.LinkKeySig,\n\t}, nil\n}\n\nfunc (s *service) ReplicationServiceRegisterGroup(ctx context.Context, request *protocoltypes.ReplicationServiceRegisterGroup_Request) (_ *protocoltypes.ReplicationServiceRegisterGroup_Reply, err error) {\n\tctx, _, endSection := tyber.Section(ctx, s.logger, \"Registering replication service for group\")\n\tdefer func() { endSection(err, \"\") }()\n\n\tif request.GroupPk == nil {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"invalid GroupPK\"))\n\t}\n\n\tif request.Token == \"\" {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"invalid token\"))\n\t}\n\n\tif request.ReplicationServer == \"\" {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"invalid replication server\"))\n\t}\n\n\tgc, err := s.GetContextGroupForID(request.GroupPk)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(err)\n\t}\n\n\treplGroup, err := FilterGroupForReplication(gc.group)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_TODO.Wrap(err)\n\t}\n\n\taccountGroup := s.getAccountGroup()\n\tif accountGroup == nil {\n\t\treturn nil, errcode.ErrCode_ErrGroupMissing\n\t}\n\n\tgopts := []grpc.DialOption{\n\t\tgrpc.WithPerRPCCredentials(grpcutil.NewUnsecureSimpleAuthAccess(\"bearer\", request.Token)),\n\t}\n\n\tif s.grpcInsecure {\n\t\tgopts = append(gopts, grpc.WithTransportCredentials(insecure.NewCredentials()))\n\t} else {\n\t\ttlsconfig := credentials.NewTLS(&tls.Config{\n\t\t\tMinVersion: tls.VersionTLS12,\n\t\t})\n\t\tgopts = append(gopts, grpc.WithTransportCredentials(tlsconfig))\n\t}\n\n\tcc, err := grpc.NewClient(\"passthrough://\"+request.ReplicationServer, gopts...)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrStreamWrite.Wrap(err)\n\t}\n\n\tclient := replicationtypes.NewReplicationServiceClient(cc)\n\n\tif _, err = client.ReplicateGroup(ctx, &replicationtypes.ReplicationServiceReplicateGroup_Request{\n\t\tGroup: replGroup,\n\t}); err != nil {\n\t\treturn nil, errcode.ErrCode_ErrServiceReplicationServer.Wrap(err)\n\t}\n\n\ts.logger.Info(\"group will be replicated\", logutil.PrivateString(\"public-key\", base64.RawURLEncoding.EncodeToString(request.GroupPk)))\n\n\tif _, err := gc.metadataStore.SendGroupReplicating(ctx, request.AuthenticationUrl, request.ReplicationServer); err != nil {\n\t\ts.logger.Error(\"error while notifying group about replication\", zap.Error(err))\n\t}\n\n\treturn &protocoltypes.ReplicationServiceRegisterGroup_Reply{}, nil\n}\n"
  },
  {
    "path": "api_verified_credentials.go",
    "content": "package weshnet\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"strings\"\n\t\"time\"\n\n\t\"berty.tech/weshnet/v2/pkg/bertyvcissuer\"\n\t\"berty.tech/weshnet/v2/pkg/cryptoutil\"\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n)\n\nfunc (s *service) CredentialVerificationServiceInitFlow(ctx context.Context, request *protocoltypes.CredentialVerificationServiceInitFlow_Request) (*protocoltypes.CredentialVerificationServiceInitFlow_Reply, error) {\n\ts.lock.Lock()\n\ts.vcClient = bertyvcissuer.NewClient(request.ServiceUrl)\n\tclient := s.vcClient\n\ts.lock.Unlock()\n\n\tctx, cancel := context.WithTimeout(ctx, time.Second*10)\n\tdefer cancel()\n\n\t// TODO: allow selection of alt-scoped keys\n\t// TODO: avoid exporting account keys\n\tpkRaw, err := s.accountGroupCtx.ownMemberDevice.Member().Raw()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput\n\t}\n\n\tif !bytes.Equal(pkRaw, request.PublicKey) {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput\n\t}\n\n\turl, err := client.Init(ctx, request.Link, cryptoutil.NewFuncSigner(s.accountGroupCtx.ownMemberDevice.Member(), s.accountGroupCtx.ownMemberDevice.MemberSign))\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\n\treturn &protocoltypes.CredentialVerificationServiceInitFlow_Reply{\n\t\tUrl:       url,\n\t\tSecureUrl: strings.HasPrefix(url, \"https://\"),\n\t}, nil\n}\n\nfunc (s *service) CredentialVerificationServiceCompleteFlow(ctx context.Context, request *protocoltypes.CredentialVerificationServiceCompleteFlow_Request) (*protocoltypes.CredentialVerificationServiceCompleteFlow_Reply, error) {\n\ts.lock.Lock()\n\tclient := s.vcClient\n\ts.lock.Unlock()\n\n\tif client == nil {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"a verification flow needs to be started first\"))\n\t}\n\n\tcredentials, identifier, parsedCredential, err := client.Complete(request.CallbackUri)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\n\t_, err = s.accountGroupCtx.metadataStore.SendAccountVerifiedCredentialAdded(ctx, &protocoltypes.AccountVerifiedCredentialRegistered{\n\t\tVerifiedCredential: credentials,\n\t\tRegistrationDate:   parsedCredential.Issued.UnixNano(),\n\t\tExpirationDate:     parsedCredential.Expired.UnixNano(),\n\t\tIdentifier:         identifier,\n\t\tIssuer:             parsedCredential.Issuer.ID,\n\t})\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\n\treturn &protocoltypes.CredentialVerificationServiceCompleteFlow_Reply{\n\t\tIdentifier: identifier,\n\t}, nil\n}\n\nfunc (s *service) VerifiedCredentialsList(request *protocoltypes.VerifiedCredentialsList_Request, server protocoltypes.ProtocolService_VerifiedCredentialsListServer) error {\n\tnow := time.Now().UnixNano()\n\tcredentials := s.accountGroupCtx.metadataStore.ListVerifiedCredentials()\n\n\tfor _, credential := range credentials {\n\t\tif request.FilterIdentifier != \"\" && credential.Identifier != request.FilterIdentifier {\n\t\t\tcontinue\n\t\t}\n\n\t\tif request.ExcludeExpired && credential.ExpirationDate < now {\n\t\t\tcontinue\n\t\t}\n\n\t\tif request.FilterIssuer != \"\" && credential.Issuer != request.FilterIssuer {\n\t\t\tcontinue\n\t\t}\n\n\t\tif err := server.Send(&protocoltypes.VerifiedCredentialsList_Reply{\n\t\t\tCredential: credential,\n\t\t}); err != nil {\n\t\t\treturn errcode.ErrCode_ErrStreamWrite.Wrap(err)\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "blackbox_test.go",
    "content": "package weshnet_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"berty.tech/weshnet/v2\"\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n\t\"berty.tech/weshnet/v2/pkg/secretstore\"\n\t\"berty.tech/weshnet/v2/pkg/testutil\"\n)\n\nfunc TestTestingClient_impl(t *testing.T) {\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\tlogger, cleanup := testutil.Logger(t)\n\tdefer cleanup()\n\n\tsecretStore, err := secretstore.NewInMemSecretStore(nil)\n\trequire.NoError(t, err)\n\n\tclient, cleanup := weshnet.TestingService(ctx, t, weshnet.Opts{\n\t\tLogger:      logger,\n\t\tSecretStore: secretStore,\n\t})\n\tdefer cleanup()\n\n\t// test service\n\t_, _ = client.ServiceGetConfiguration(ctx, &protocoltypes.ServiceGetConfiguration_Request{})\n\tstatus := client.Status()\n\texpected := weshnet.Status{}\n\tassert.Equal(t, expected, status)\n}\n\nfunc ExampleNewInMemoryServiceClient_basic() {\n\t// disable resources manager for test\n\tos.Setenv(\"LIBP2P_RCMGR\", \"false\")\n\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\tclient, err := weshnet.NewInMemoryServiceClient()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer client.Close()\n\n\tret, err := client.ServiceGetConfiguration(ctx, &protocoltypes.ServiceGetConfiguration_Request{})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfor _, listener := range ret.Listeners {\n\t\tif listener == \"/p2p-circuit\" {\n\t\t\tfmt.Println(listener)\n\t\t}\n\t}\n\n\t// Output:\n\t// /p2p-circuit\n}\n\nfunc ExampleNewPersistentServiceClient_basic() {\n\t// disable resources manager for test\n\tos.Setenv(\"LIBP2P_RCMGR\", \"false\")\n\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\t// create a temporary path to host data of our persistent service\n\tpath, err := os.MkdirTemp(\"\", \"weshnet-test-persistent\")\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer os.RemoveAll(path)\n\n\tvar peerid string\n\t// open once\n\t{\n\t\tclient, err := weshnet.NewPersistentServiceClient(path)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\n\t\tret, err := client.ServiceGetConfiguration(ctx, &protocoltypes.ServiceGetConfiguration_Request{})\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\n\t\tpeerid = ret.PeerId\n\n\t\tif err := client.Close(); err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t}\n\n\t// open twice\n\t{\n\t\tclient, err := weshnet.NewPersistentServiceClient(path)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tdefer client.Close()\n\n\t\tret, err := client.ServiceGetConfiguration(ctx, &protocoltypes.ServiceGetConfiguration_Request{})\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\n\t\tif peerid != ret.PeerId {\n\t\t\tpanic(\"peerid should be identical\")\n\t\t}\n\t}\n\n\t// Output:\n}\n\nfunc ExampleNewServiceClient_basic() {\n\t// disable resources manager for test\n\tos.Setenv(\"LIBP2P_RCMGR\", \"false\")\n\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\tclient, err := weshnet.NewServiceClient(weshnet.Opts{})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer client.Close()\n\n\tret, err := client.ServiceGetConfiguration(ctx, &protocoltypes.ServiceGetConfiguration_Request{})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfor _, listener := range ret.Listeners {\n\t\tif listener == \"/p2p-circuit\" {\n\t\t\tfmt.Println(listener)\n\t\t}\n\t}\n\n\t// Output:\n\t// /p2p-circuit\n}\n\nfunc ExampleNewService_basic() {\n\t// disable resources manager for test\n\tos.Setenv(\"LIBP2P_RCMGR\", \"false\")\n\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\tclient, err := weshnet.NewService(weshnet.Opts{})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer client.Close()\n\n\tret, err := client.ServiceGetConfiguration(ctx, &protocoltypes.ServiceGetConfiguration_Request{})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfor _, listener := range ret.Listeners {\n\t\tif listener == \"/p2p-circuit\" {\n\t\t\tfmt.Println(listener)\n\t\t}\n\t}\n\n\t// Output:\n\t// /p2p-circuit\n}\n\n// FIXME: create examples that actually use groups and contacts\n"
  },
  {
    "path": "buf.gen.tag.yaml",
    "content": "version: v1\nplugins:\n    - name: gotag\n      out: ./\n      opt: module=berty.tech/weshnet/v2\n"
  },
  {
    "path": "buf.gen.yaml",
    "content": "version: v2\nplugins:\n    - local: protoc-gen-go\n      out: ./\n      opt: module=berty.tech/weshnet/v2\n    - local: protoc-gen-go-grpc\n      out: ./\n      opt: module=berty.tech/weshnet/v2\n    - local: protoc-gen-grpc-gateway\n      out: ./\n      opt:\n          - module=berty.tech/weshnet/v2\n          - generate_unbound_methods=true\n"
  },
  {
    "path": "connectedness_manager.go",
    "content": "package weshnet\n\nimport (\n\t\"context\"\n\t\"sync\"\n\n\tpeer \"github.com/libp2p/go-libp2p/core/peer\"\n\n\t\"berty.tech/weshnet/v2/internal/notify\"\n)\n\ntype ConnectednessType int\n\nconst (\n\tConnectednessTypeDisconnected ConnectednessType = iota\n\tConnectednessTypeReconnecting\n\tConnectednessTypeConnected\n)\n\ntype ConnectednessUpdate struct {\n\tPeer   peer.ID\n\tStatus ConnectednessType\n}\n\ntype PeersConnectedness map[peer.ID]ConnectednessType\n\ntype GroupStatus struct {\n\tpeers  map[peer.ID]*PeerStatus\n\tnotify *notify.Notify\n}\n\ntype PeerStatus struct {\n\tgroups map[string]*GroupStatus\n\tstatus ConnectednessType\n}\n\ntype ConnectednessManager struct {\n\tpeerState  map[peer.ID]*PeerStatus\n\tgroupState map[string]*GroupStatus\n\tmuState    sync.Mutex\n}\n\nfunc NewConnectednessManager() *ConnectednessManager {\n\treturn &ConnectednessManager{\n\t\tpeerState:  make(map[peer.ID]*PeerStatus),\n\t\tgroupState: make(map[string]*GroupStatus),\n\t}\n}\n\n// AssociatePeer associate a peer to a group\nfunc (m *ConnectednessManager) AssociatePeer(group string, peer peer.ID) {\n\tm.muState.Lock()\n\tdefer m.muState.Unlock()\n\n\tsg := m.getGroupStatus(group)\n\tsp := m.getPeerStatus(peer)\n\n\tsg.notify.L.Lock()\n\tif _, ok := sg.peers[peer]; !ok {\n\t\t// we got a new peer, update and signal an update\n\t\tsg.peers[peer] = sp\n\t\tsp.groups[group] = sg\n\t\tsg.notify.Broadcast()\n\t}\n\tsg.notify.L.Unlock()\n}\n\n// UpdateState update peer current connectedness state\nfunc (m *ConnectednessManager) UpdateState(peer peer.ID, update ConnectednessType) {\n\tm.muState.Lock()\n\tdefer m.muState.Unlock()\n\n\tsp := m.getPeerStatus(peer)\n\tif sp.status != update {\n\t\tsp.status = update\n\n\t\t// notify each group that need an update\n\t\tfor _, g := range sp.groups {\n\t\t\tg.notify.Broadcast()\n\t\t}\n\t}\n}\n\n// WaitForConnectednessChange wait until the given `current` peers status differ from `local` peers state\nfunc (m *ConnectednessManager) WaitForConnectednessChange(ctx context.Context, gkey string, current PeersConnectedness) ([]peer.ID, bool) {\n\tm.muState.Lock()\n\tsg := m.getGroupStatus(gkey)\n\tm.muState.Unlock()\n\n\tok := true\n\tsg.notify.L.Lock()\n\tvar updated []peer.ID\n\tfor ok {\n\t\t// check if there are some diff between local state and the current state\n\t\tif updated = m.updateStatus(sg, current); len(updated) > 0 {\n\t\t\tbreak // we got some update, leave the loop\n\t\t}\n\n\t\t// wait until there is an update on this group or context expire\n\t\t// unlock notify locker\n\t\tok = sg.notify.Wait(ctx)\n\t}\n\n\tsg.notify.L.Unlock()\n\n\treturn updated, ok\n}\n\nfunc (m *ConnectednessManager) getGroupStatus(gkey string) *GroupStatus {\n\ts, ok := m.groupState[gkey]\n\tif !ok {\n\t\ts = &GroupStatus{\n\t\t\tpeers:  make(map[peer.ID]*PeerStatus),\n\t\t\tnotify: notify.New(&sync.Mutex{}),\n\t\t}\n\t\tm.groupState[gkey] = s\n\t}\n\treturn s\n}\n\nfunc (m *ConnectednessManager) getPeerStatus(peer peer.ID) *PeerStatus {\n\ts, ok := m.peerState[peer]\n\tif !ok {\n\t\ts = &PeerStatus{\n\t\t\tgroups: make(map[string]*GroupStatus),\n\t\t}\n\t\tm.peerState[peer] = s\n\t}\n\treturn s\n}\n\nfunc (m *ConnectednessManager) updateStatus(group *GroupStatus, current PeersConnectedness) []peer.ID {\n\tm.muState.Lock()\n\n\tupdated := []peer.ID{}\n\tfor peer := range group.peers {\n\t\tif ourPeer, ok := m.peerState[peer]; ok {\n\t\t\ttheirStatus, ok := current[peer]\n\t\t\tif ok && ourPeer.status == theirStatus {\n\t\t\t\tcontinue // we share the same state for that peer, skip\n\t\t\t}\n\n\t\t\t// update peer status\n\t\t\tcurrent[peer] = ourPeer.status\n\t\t\tupdated = append(updated, peer)\n\t\t}\n\t}\n\n\tm.muState.Unlock()\n\n\treturn updated\n}\n"
  },
  {
    "path": "consts.go",
    "content": "package weshnet\n\nimport (\n\t\"berty.tech/go-orbit-db/cache/cacheleveldown\"\n)\n\nconst (\n\tNamespaceOrbitDBDatastore = \"orbitdb_datastore\"\n\tNamespaceOrbitDBDirectory = \"orbitdb\"\n\tNamespaceIPFSDatastore    = \"ipfs_datastore\"\n)\n\nvar InMemoryDirectory = cacheleveldown.InMemoryDirectory\n"
  },
  {
    "path": "contact_request_manager.go",
    "content": "package weshnet\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/base64\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\tipfscid \"github.com/ipfs/go-cid\"\n\t\"github.com/libp2p/go-libp2p/core/crypto\"\n\t\"github.com/libp2p/go-libp2p/core/network\"\n\tpeer \"github.com/libp2p/go-libp2p/core/peer\"\n\t\"github.com/libp2p/go-libp2p/p2p/host/eventbus\"\n\t\"go.uber.org/zap\"\n\t\"google.golang.org/protobuf/proto\"\n\n\t\"berty.tech/weshnet/v2/internal/handshake\"\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n\t\"berty.tech/weshnet/v2/pkg/ipfsutil\"\n\t\"berty.tech/weshnet/v2/pkg/logutil\"\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n\t\"berty.tech/weshnet/v2/pkg/protoio\"\n\t\"berty.tech/weshnet/v2/pkg/tyber\"\n)\n\nconst contactRequestV1 = \"/wesh/contact_req/1.0.0\"\n\ntype contactRequestsManager struct {\n\tmuManager sync.Mutex\n\n\tctx            context.Context\n\tcancel         context.CancelFunc\n\tannounceCancel context.CancelFunc\n\n\tlookupProcess   map[string]context.CancelFunc\n\tmuLookupProcess sync.Mutex\n\n\tlogger *zap.Logger\n\n\tenabled bool\n\n\townRendezvousSeed []byte\n\taccountPrivateKey crypto.PrivKey\n\n\tipfs          ipfsutil.ExtendedCoreAPI\n\tswiper        *Swiper\n\tmetadataStore *MetadataStore\n}\n\nfunc newContactRequestsManager(s *Swiper, store *MetadataStore, ipfs ipfsutil.ExtendedCoreAPI, logger *zap.Logger) (*contactRequestsManager, error) {\n\taccountPrivateKey, err := store.secretStore.GetAccountPrivateKey()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tctx, cancel := context.WithCancel(context.Background())\n\n\tcm := &contactRequestsManager{\n\t\tlookupProcess:     make(map[string]context.CancelFunc),\n\t\tmetadataStore:     store,\n\t\tipfs:              ipfs,\n\t\tlogger:            logger.Named(\"req-mngr\"),\n\t\taccountPrivateKey: accountPrivateKey,\n\t\tctx:               ctx,\n\t\tcancel:            cancel,\n\t\tswiper:            s,\n\t}\n\n\tgo cm.metadataWatcher(ctx)\n\n\treturn cm, nil\n}\n\nfunc (c *contactRequestsManager) close() {\n\tif c.isClosed() {\n\t\tc.logger.Warn(\"contactRequestsManager already closed\")\n\t\treturn\n\t}\n\n\tc.cancel()\n\n\tc.muManager.Lock()\n\tdefer c.muManager.Unlock()\n\n\tc.enabled = false\n\n\tc.disableAnnounce()\n\n\tc.ipfs.RemoveStreamHandler(contactRequestV1)\n}\n\nfunc (c *contactRequestsManager) isClosed() bool {\n\tselect {\n\tcase <-c.ctx.Done():\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\nfunc (c *contactRequestsManager) metadataWatcher(ctx context.Context) {\n\thandlers := map[protocoltypes.EventType]func(context.Context, *protocoltypes.GroupMetadataEvent) error{\n\t\tprotocoltypes.EventType_EventTypeAccountContactRequestDisabled:         c.metadataRequestDisabled,\n\t\tprotocoltypes.EventType_EventTypeAccountContactRequestEnabled:          c.metadataRequestEnabled,\n\t\tprotocoltypes.EventType_EventTypeAccountContactRequestReferenceReset:   c.metadataRequestReset,\n\t\tprotocoltypes.EventType_EventTypeAccountContactRequestOutgoingEnqueued: c.metadataRequestEnqueued,\n\n\t\t// @FIXME: looks like we don't need those events\n\t\tprotocoltypes.EventType_EventTypeAccountContactRequestOutgoingSent:     c.metadataRequestSent,\n\t\tprotocoltypes.EventType_EventTypeAccountContactRequestIncomingReceived: c.metadataRequestReceived,\n\t}\n\n\t// subscribe to new event\n\tsub, err := c.metadataStore.EventBus().Subscribe(new(*protocoltypes.GroupMetadataEvent),\n\t\teventbus.Name(\"weshnet/rqmngr/metadata-watcher\"))\n\tif err != nil {\n\t\tc.logger.Warn(\"unable to subscribe to group metadata event\", zap.Error(err))\n\t\treturn\n\t}\n\n\t// recreate previous contact request state\n\tenabled, contact := c.metadataStore.GetIncomingContactRequestsStatus()\n\tif contact != nil {\n\t\tc.ownRendezvousSeed = contact.PublicRendezvousSeed\n\t}\n\n\tc.muManager.Lock()\n\tif enabled {\n\t\tif err := c.enableContactRequest(ctx); err != nil {\n\t\t\tc.logger.Warn(\"unable to enable contact request\", zap.Error(err))\n\t\t}\n\t}\n\tc.muManager.Unlock()\n\n\t// enqueue all contact with the `ToRequest` state\n\tfor _, contact := range c.metadataStore.ListContactsByStatus(protocoltypes.ContactState_ContactStateToRequest) {\n\t\tif err := c.enqueueRequest(ctx, contact); err != nil {\n\t\t\tc.logger.Warn(\"unable to enqueue contact request\", logutil.PrivateBinary(\"pk\", contact.Pk), zap.Error(err))\n\t\t}\n\t}\n\n\tdefer sub.Close()\n\tfor {\n\t\tvar evt any\n\t\tselect {\n\t\tcase evt = <-sub.Out():\n\t\tcase <-ctx.Done():\n\t\t\treturn\n\t\t}\n\n\t\t// handle new events\n\t\te := evt.(*protocoltypes.GroupMetadataEvent)\n\t\ttyp := e.GetMetadata().GetEventType()\n\t\thctx, _, endSection := tyber.Section(ctx, c.logger, fmt.Sprintf(\"handling event - %s\", typ.String()))\n\n\t\tc.muManager.Lock()\n\n\t\tvar err error\n\t\tif handler, ok := handlers[typ]; ok {\n\t\t\tif err = handler(hctx, e); err != nil {\n\t\t\t\tc.logger.Error(\"metadata store event handler\", zap.String(\"event\", typ.String()), zap.Error(err))\n\t\t\t}\n\t\t}\n\n\t\tc.muManager.Unlock()\n\n\t\tendSection(err, \"\")\n\t}\n}\n\nfunc (c *contactRequestsManager) metadataRequestDisabled(_ context.Context, _ *protocoltypes.GroupMetadataEvent) error {\n\tif !c.enabled {\n\t\tc.logger.Warn(\"contact request already disabled\")\n\t\treturn nil\n\t}\n\n\tc.enabled = false\n\n\tc.disableAnnounce()\n\n\tc.ipfs.RemoveStreamHandler(contactRequestV1)\n\n\treturn nil\n}\n\nfunc (c *contactRequestsManager) metadataRequestEnabled(ctx context.Context, evt *protocoltypes.GroupMetadataEvent) error {\n\te := &protocoltypes.AccountContactRequestEnabled{}\n\tif err := proto.Unmarshal(evt.Event, e); err != nil {\n\t\treturn errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\treturn c.enableContactRequest(ctx)\n}\n\nfunc (c *contactRequestsManager) enableContactRequest(ctx context.Context) error {\n\tif c.enabled {\n\t\tc.logger.Warn(\"contact request already enabled\")\n\t\treturn nil\n\t}\n\n\tpkBytes, err := c.accountPrivateKey.GetPublic().Raw()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to get raw pk: %w\", err)\n\t}\n\n\tc.ipfs.SetStreamHandler(contactRequestV1, func(s network.Stream) {\n\t\tctx, _, endSection := tyber.Section(c.ctx, c.logger, \"receiving incoming contact request\")\n\n\t\tif err := c.handleIncomingRequest(ctx, s); err != nil {\n\t\t\tc.logger.Error(\"unable to handle incoming contact request\", zap.Error(err))\n\t\t}\n\n\t\tendSection(err, \"\")\n\n\t\tif err := s.Reset(); err != nil {\n\t\t\tc.logger.Error(\"unable to reset stream\", zap.Error(err))\n\t\t}\n\t})\n\n\tc.enabled = true\n\ttyber.LogStep(ctx, c.logger, \"enabled contact request\")\n\n\t// announce on swiper if we already got seed\n\tif c.ownRendezvousSeed != nil {\n\t\treturn c.enableAnnounce(ctx, c.ownRendezvousSeed, pkBytes)\n\t}\n\n\tc.logger.Warn(\"no seed registered, reset will be needed before announcing\")\n\treturn nil\n}\n\nfunc (c *contactRequestsManager) metadataRequestReset(ctx context.Context, evt *protocoltypes.GroupMetadataEvent) error {\n\te := &protocoltypes.AccountContactRequestReferenceReset{}\n\tif err := proto.Unmarshal(evt.Event, e); err != nil {\n\t\treturn errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\taccPK, err := c.accountPrivateKey.GetPublic().Raw()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to get raw pk: %w\", err)\n\t}\n\n\tswitch {\n\tcase e.PublicRendezvousSeed == nil:\n\t\treturn fmt.Errorf(\"unable to reset with an empty seed\")\n\tcase bytes.Equal(e.PublicRendezvousSeed, c.ownRendezvousSeed):\n\t\treturn fmt.Errorf(\"unable to reset twice with the same seed\")\n\t}\n\n\t// updating rendezvous seed\n\ttyber.LogStep(ctx, c.logger, \"update rendezvous seed\")\n\tc.ownRendezvousSeed = e.PublicRendezvousSeed\n\n\t// if contact request manager is disable don't run announce\n\tif !c.enabled {\n\t\treturn nil\n\t}\n\n\treturn c.enableAnnounce(ctx, c.ownRendezvousSeed, accPK)\n}\n\nfunc (c *contactRequestsManager) metadataRequestEnqueued(ctx context.Context, evt *protocoltypes.GroupMetadataEvent) error {\n\tctx = tyber.ContextWithConstantTraceID(ctx, \"msgrcvd-\"+cidBytesString(evt.EventContext.Id))\n\n\ttraceName := fmt.Sprintf(\"Received %s on group %s\",\n\t\tstrings.TrimPrefix(evt.Metadata.EventType.String(), \"EventType\"), base64.RawURLEncoding.EncodeToString(evt.EventContext.GroupPk))\n\tc.logger.Debug(traceName, tyber.FormatStepLogFields(ctx, []tyber.Detail{}, tyber.UpdateTraceName(traceName))...)\n\n\te := &protocoltypes.AccountContactRequestOutgoingEnqueued{}\n\tif err := proto.Unmarshal(evt.Event, e); err != nil {\n\t\treturn tyber.LogError(ctx, c.logger, \"Failed to unmarshal event\", err)\n\t}\n\n\t// enqueue contact request\n\tif err := c.enqueueRequest(ctx, e.Contact); err != nil {\n\t\treturn tyber.LogError(ctx, c.logger, \"Failed to enqueue request\", err)\n\t}\n\n\treturn nil\n}\n\nfunc (c *contactRequestsManager) metadataRequestSent(_ context.Context, evt *protocoltypes.GroupMetadataEvent) error {\n\te := &protocoltypes.AccountContactRequestOutgoingSent{}\n\tif err := proto.Unmarshal(evt.Event, e); err != nil {\n\t\treturn errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\t// another device may have successfully sent contact request, try to cancel\n\t// lookup if needed\n\tc.cancelContactLookup(e.ContactPk)\n\treturn nil\n}\n\nfunc (c *contactRequestsManager) metadataRequestReceived(_ context.Context, evt *protocoltypes.GroupMetadataEvent) error {\n\te := &protocoltypes.AccountContactRequestIncomingReceived{}\n\tif err := proto.Unmarshal(evt.Event, e); err != nil {\n\t\treturn errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\t// another device may have successfully sent contact request, try to cancel\n\t// lookup if needed\n\tc.cancelContactLookup(e.ContactPk)\n\treturn nil\n}\n\nfunc (c *contactRequestsManager) registerContactLookup(ctx context.Context, contactPK []byte) context.Context {\n\tc.muLookupProcess.Lock()\n\n\tkey := hex.EncodeToString(contactPK)\n\n\t// make sure to only have one process for this pk running\n\tctx, cancel := context.WithCancel(ctx)\n\tif cancelProvious, ok := c.lookupProcess[key]; ok {\n\t\tcancelProvious() // cancel previous lookup if needed\n\t}\n\tc.lookupProcess[key] = cancel\n\n\tc.muLookupProcess.Unlock()\n\treturn ctx\n}\n\nfunc (c *contactRequestsManager) cancelContactLookup(contactPK []byte) {\n\tc.muLookupProcess.Lock()\n\n\tkey := hex.EncodeToString(contactPK)\n\n\t// cancel current lookup if needed\n\tif cancel, ok := c.lookupProcess[key]; ok {\n\t\tcancel()\n\t\tdelete(c.lookupProcess, key)\n\t}\n\n\tc.muLookupProcess.Unlock()\n}\n\nfunc (c *contactRequestsManager) enableAnnounce(ctx context.Context, seed, accPK []byte) error {\n\tif seed == nil {\n\t\treturn fmt.Errorf(\"announcing with empty seed\")\n\t}\n\n\tif c.announceCancel != nil { // is already enable\n\t\ttyber.LogStep(ctx, c.logger, \"canceling previous announce\")\n\t\tc.announceCancel()\n\t}\n\n\tctx, c.announceCancel = context.WithCancel(ctx)\n\tc.enabled = true\n\n\ttyber.LogStep(ctx, c.logger, \"announcing on swipper\")\n\n\t// start announcing on swiper, this method should take care ton announce as\n\t// many time as needed\n\tc.swiper.Announce(ctx, accPK, seed)\n\treturn nil\n}\n\nfunc (c *contactRequestsManager) disableAnnounce() {\n\tif c.announceCancel != nil {\n\t\tc.announceCancel()\n\t\tc.announceCancel = nil\n\t}\n}\n\nfunc (c *contactRequestsManager) enqueueRequest(ctx context.Context, to *protocoltypes.ShareableContact) (err error) {\n\tctx, _, endSection := tyber.Section(ctx, c.logger, \"Enqueue contact request: \"+base64.RawURLEncoding.EncodeToString(to.Pk))\n\n\totherPK, err := crypto.UnmarshalEd25519PublicKey(to.Pk)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif ok := c.metadataStore.checkContactStatus(otherPK, protocoltypes.ContactState_ContactStateAdded); ok {\n\t\terr = fmt.Errorf(\"contact already added\")\n\t\tendSection(err, \"\")\n\t\t// contact already added,\n\t\treturn err\n\t}\n\n\t// register lookup process\n\tctx = c.registerContactLookup(ctx, to.Pk)\n\n\t// start watching topic on swiper, this method should take care of calling\n\t// `FindPeer` as many times as needed\n\tcpeers := c.swiper.WatchTopic(ctx, to.Pk, to.PublicRendezvousSeed)\n\tgo func() {\n\t\tvar err error\n\t\tfor peer := range cpeers {\n\t\t\t// get our sharable contact to send to other contact\n\t\t\tif err = c.SendContactRequest(ctx, to, otherPK, peer); err != nil {\n\t\t\t\tc.logger.Warn(\"unable to send contact request\", zap.Error(err))\n\t\t\t} else {\n\t\t\t\t// successfully send contact request, leave the loop and cancel lookup\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\t// wait one second to avoid infinity loop on send contact request\n\t\t\t// ex: when we dont have any network, send request can fail instantly\n\t\t\ttime.Sleep(time.Second)\n\t\t}\n\n\t\t// cancel lookup process\n\t\tc.cancelContactLookup(to.Pk)\n\n\t\tendSection(err, \"\")\n\t}()\n\n\treturn nil\n}\n\n// SendContactRequest try to perform contact request with the given remote peer\nfunc (c *contactRequestsManager) SendContactRequest(ctx context.Context, to *protocoltypes.ShareableContact, otherPK crypto.PubKey, peer peer.AddrInfo) (err error) {\n\tctx, _, endSection := tyber.Section(ctx, c.logger, \"sending contact request\")\n\tdefer func() {\n\t\tendSection(err, \"\")\n\t}()\n\n\t_, own := c.metadataStore.GetIncomingContactRequestsStatus()\n\tif own == nil {\n\t\terr = fmt.Errorf(\"unable to retrieve own contact information\")\n\t\treturn err\n\t}\n\n\t// get own metadata for contact\n\townMetadata, err := c.metadataStore.GetRequestOwnMetadataForContact(to.Pk)\n\tif err != nil {\n\t\tc.logger.Warn(\"unable to get own metadata for contact\", zap.Error(err))\n\t\townMetadata = nil\n\t}\n\town.Metadata = ownMetadata\n\n\t// make sure to have connection with the remote peer\n\tif err := c.ipfs.Swarm().Connect(ctx, peer); err != nil {\n\t\treturn fmt.Errorf(\"unable to connect: %w\", err)\n\t}\n\n\t// create a new stream with the remote peer\n\tstream, err := c.ipfs.NewStream(network.WithAllowLimitedConn(ctx, \"req_mngr\"), peer.ID, contactRequestV1)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to open stream: %w\", err)\n\t}\n\n\tdefer func() {\n\t\tif err := stream.Close(); err != nil {\n\t\t\tc.logger.Warn(\"error while closing stream with other peer\", zap.Error(err))\n\t\t}\n\t}()\n\n\treader := protoio.NewDelimitedReader(stream, 2048)\n\twriter := protoio.NewDelimitedWriter(stream)\n\n\tc.logger.Debug(\"performing handshake\")\n\n\ttyber.LogStep(ctx, c.logger, \"performing handshake\")\n\tif err := handshake.RequestUsingReaderWriter(ctx, c.logger, reader, writer, c.accountPrivateKey, otherPK); err != nil {\n\t\treturn fmt.Errorf(\"an error occurred during handshake: %w\", err)\n\t}\n\n\ttyber.LogStep(ctx, c.logger, \"sending own contact\")\n\t// send own contact information\n\tif err := writer.WriteMsg(own); err != nil {\n\t\treturn fmt.Errorf(\"an error occurred while sending own contact information: %w\", err)\n\t}\n\n\ttyber.LogStep(ctx, c.logger, \"mark contact request has sent\")\n\t// mark this contact request as sent\n\tif _, err := c.metadataStore.ContactRequestOutgoingSent(ctx, otherPK); err != nil {\n\t\treturn fmt.Errorf(\"an error occurred while marking contact request as sent: %w\", err)\n\t}\n\n\treturn nil\n}\n\nfunc (c *contactRequestsManager) handleIncomingRequest(ctx context.Context, stream network.Stream) (err error) {\n\treader := protoio.NewDelimitedReader(stream, 2048)\n\twriter := protoio.NewDelimitedWriter(stream)\n\n\ttyber.LogStep(ctx, c.logger, \"responding to handshake\")\n\n\totherPK, err := handshake.ResponseUsingReaderWriter(ctx, c.logger, reader, writer, c.accountPrivateKey)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"handshake failed: %w\", err)\n\t}\n\n\totherPKBytes, err := otherPK.Raw()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to marshal contact public key: %w\", err)\n\t}\n\n\tcontact := &protocoltypes.ShareableContact{}\n\n\ttyber.LogStep(ctx, c.logger, \"checking remote contact information\")\n\n\t// read remote contact information\n\tif err := reader.ReadMsg(contact); err != nil {\n\t\treturn fmt.Errorf(\"failed to read contact information: %w\", err)\n\t}\n\n\t// validate contact pk\n\tif !bytes.Equal(otherPKBytes, contact.Pk) {\n\t\treturn fmt.Errorf(\"contact information does not match handshake data\")\n\t}\n\n\t// check contact information format\n\tif err := contact.CheckFormat(protocoltypes.ShareableContactOptionsAllowMissingRDVSeed); err != nil {\n\t\treturn fmt.Errorf(\"invalid contact information format: %w\", err)\n\t}\n\n\ttyber.LogStep(ctx, c.logger, \"marking contact request has received\")\n\n\t// mark contact request as received\n\t_, err = c.metadataStore.ContactRequestIncomingReceived(ctx, &protocoltypes.ShareableContact{\n\t\tPk:                   otherPKBytes,\n\t\tPublicRendezvousSeed: contact.PublicRendezvousSeed,\n\t\tMetadata:             contact.Metadata,\n\t})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"invalid contact information format: %w\", err)\n\t}\n\n\treturn nil\n}\n\nfunc cidBytesString(bytes []byte) string {\n\tcid, err := ipfscid.Cast(bytes)\n\tif err != nil {\n\t\treturn \"error\"\n\t}\n\treturn cid.String()\n}\n"
  },
  {
    "path": "contact_request_manager_test.go",
    "content": "package weshnet\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"testing\"\n\t\"time\"\n\n\tmocknet \"github.com/libp2p/go-libp2p/p2p/net/mock\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"google.golang.org/protobuf/proto\"\n\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n\t\"berty.tech/weshnet/v2/pkg/testutil\"\n)\n\nfunc TestContactRequestFlow(t *testing.T) {\n\ttestutil.FilterSpeed(t, testutil.Slow)\n\n\tctx, cancel := context.WithTimeout(context.Background(), time.Second*20)\n\tdefer cancel()\n\tlogger, cleanup := testutil.Logger(t)\n\tdefer cleanup()\n\n\topts := TestingOpts{\n\t\tMocknet: mocknet.New(),\n\t\tLogger:  logger,\n\t}\n\n\tmetadataSender1 := []byte(\"sender_1\")\n\n\tpts, cleanup := NewTestingProtocolWithMockedPeers(ctx, t, &opts, nil, 2)\n\tdefer cleanup()\n\n\t_, err := pts[0].Client.ContactRequestEnable(ctx, &protocoltypes.ContactRequestEnable_Request{})\n\trequire.NoError(t, err)\n\n\t_, err = pts[1].Client.ContactRequestEnable(ctx, &protocoltypes.ContactRequestEnable_Request{})\n\trequire.NoError(t, err)\n\n\tconfig0, err := pts[0].Client.ServiceGetConfiguration(ctx, &protocoltypes.ServiceGetConfiguration_Request{})\n\trequire.NoError(t, err)\n\trequire.NotNil(t, config0)\n\n\tconfig1, err := pts[1].Client.ServiceGetConfiguration(ctx, &protocoltypes.ServiceGetConfiguration_Request{})\n\trequire.NoError(t, err)\n\trequire.NotNil(t, config1)\n\n\tref0, err := pts[0].Client.ContactRequestResetReference(ctx, &protocoltypes.ContactRequestResetReference_Request{})\n\trequire.NoError(t, err)\n\trequire.NotNil(t, ref0)\n\n\tref1, err := pts[1].Client.ContactRequestResetReference(ctx, &protocoltypes.ContactRequestResetReference_Request{})\n\trequire.NoError(t, err)\n\trequire.NotNil(t, ref1)\n\n\tsubCtx, subCancel := context.WithCancel(ctx)\n\tdefer subCancel()\n\n\tsubMeta0, err := pts[0].Client.GroupMetadataList(subCtx, &protocoltypes.GroupMetadataList_Request{\n\t\tGroupPk: config0.AccountGroupPk,\n\t})\n\trequire.NoError(t, err)\n\tfound := false\n\n\t_, err = pts[1].Client.ContactRequestSend(ctx, &protocoltypes.ContactRequestSend_Request{\n\t\tContact: &protocoltypes.ShareableContact{\n\t\t\tPk:                   config0.AccountPk,\n\t\t\tPublicRendezvousSeed: ref0.PublicRendezvousSeed,\n\t\t},\n\t\tOwnMetadata: metadataSender1,\n\t})\n\trequire.NoError(t, err)\n\n\tfor {\n\t\tevt, err := subMeta0.Recv()\n\t\tif err == io.EOF || subMeta0.Context().Err() != nil {\n\t\t\tbreak\n\t\t}\n\n\t\trequire.NoError(t, err)\n\n\t\tif evt == nil || evt.Metadata.EventType != protocoltypes.EventType_EventTypeAccountContactRequestIncomingReceived {\n\t\t\tcontinue\n\t\t}\n\n\t\treq := &protocoltypes.AccountContactRequestIncomingReceived{}\n\t\terr = proto.Unmarshal(evt.Event, req)\n\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, config1.AccountPk, req.ContactPk)\n\t\trequire.Equal(t, metadataSender1, req.ContactMetadata)\n\t\tfound = true\n\t\tsubCancel()\n\t}\n\n\trequire.True(t, found)\n\n\t_, err = pts[1].Client.ContactRequestAccept(ctx, &protocoltypes.ContactRequestAccept_Request{\n\t\tContactPk: config0.AccountPk,\n\t})\n\n\trequire.Error(t, err)\n\n\t_, err = pts[1].Client.ContactRequestAccept(ctx, &protocoltypes.ContactRequestAccept_Request{\n\t\tContactPk: config1.AccountPk,\n\t})\n\n\trequire.Error(t, err)\n\n\t_, err = pts[0].Client.ContactRequestAccept(ctx, &protocoltypes.ContactRequestAccept_Request{\n\t\tContactPk: config0.AccountPk,\n\t})\n\n\trequire.Error(t, err)\n\n\t_, err = pts[0].Client.ContactRequestAccept(ctx, &protocoltypes.ContactRequestAccept_Request{\n\t\tContactPk: config1.AccountPk,\n\t})\n\n\trequire.NoError(t, err)\n\n\tgrpInfo, err := pts[0].Client.GroupInfo(ctx, &protocoltypes.GroupInfo_Request{\n\t\tContactPk: config1.AccountPk,\n\t})\n\trequire.NoError(t, err)\n\n\t_, err = pts[0].Client.ActivateGroup(ctx, &protocoltypes.ActivateGroup_Request{\n\t\tGroupPk: grpInfo.Group.PublicKey,\n\t})\n\n\trequire.NoError(t, err)\n\n\t_, err = pts[1].Client.ActivateGroup(ctx, &protocoltypes.ActivateGroup_Request{\n\t\tGroupPk: grpInfo.Group.PublicKey,\n\t})\n\n\trequire.NoError(t, err)\n}\n\nfunc TestContactRequestFlowWithoutIncoming(t *testing.T) {\n\tt.Skip(\"KUBO: this test timeout, disable it for now\")\n\n\ttestutil.FilterSpeed(t, testutil.Slow)\n\n\tctx, cancel := context.WithTimeout(context.Background(), time.Second*5)\n\tdefer cancel()\n\n\tlogger, cleanup := testutil.Logger(t)\n\tdefer cleanup()\n\n\tmn := mocknet.New()\n\tdefer mn.Close()\n\n\topts := TestingOpts{\n\t\tMocknet: mn,\n\t\tLogger:  logger,\n\t}\n\n\tmetadataSender1 := []byte(\"sender_1\")\n\n\tpts, cleanup := NewTestingProtocolWithMockedPeers(ctx, t, &opts, nil, 2)\n\tdefer cleanup()\n\n\t_, err := pts[0].Client.ContactRequestEnable(ctx, &protocoltypes.ContactRequestEnable_Request{})\n\trequire.NoError(t, err)\n\n\tconfig0, err := pts[0].Client.ServiceGetConfiguration(ctx, &protocoltypes.ServiceGetConfiguration_Request{})\n\trequire.NoError(t, err)\n\trequire.NotNil(t, config0)\n\n\tconfig1, err := pts[1].Client.ServiceGetConfiguration(ctx, &protocoltypes.ServiceGetConfiguration_Request{})\n\trequire.NoError(t, err)\n\trequire.NotNil(t, config1)\n\n\tref0, err := pts[0].Client.ContactRequestResetReference(ctx, &protocoltypes.ContactRequestResetReference_Request{})\n\trequire.NoError(t, err)\n\trequire.NotNil(t, ref0)\n\n\tsubCtx, subCancel := context.WithCancel(ctx)\n\tdefer subCancel()\n\n\tsubMeta0, err := pts[0].Client.GroupMetadataList(subCtx, &protocoltypes.GroupMetadataList_Request{\n\t\tGroupPk: config0.AccountGroupPk,\n\t})\n\trequire.NoError(t, err)\n\tfound := false\n\n\t_, err = pts[1].Client.ContactRequestSend(ctx, &protocoltypes.ContactRequestSend_Request{\n\t\tContact: &protocoltypes.ShareableContact{\n\t\t\tPk:                   config0.AccountPk,\n\t\t\tPublicRendezvousSeed: ref0.PublicRendezvousSeed,\n\t\t},\n\t\tOwnMetadata: metadataSender1,\n\t})\n\trequire.NoError(t, err)\n\n\tfor {\n\t\tevt, err := subMeta0.Recv()\n\t\tif err != nil {\n\t\t\tassert.NoError(t, err)\n\t\t\tbreak\n\t\t}\n\n\t\trequire.NoError(t, err)\n\n\t\tif evt == nil || evt.Metadata.EventType != protocoltypes.EventType_EventTypeAccountContactRequestIncomingReceived {\n\t\t\tcontinue\n\t\t}\n\n\t\treq := &protocoltypes.AccountContactRequestIncomingReceived{}\n\t\terr = proto.Unmarshal(evt.Event, req)\n\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, config1.AccountPk, req.ContactPk)\n\t\trequire.Equal(t, metadataSender1, req.ContactMetadata)\n\t\tfound = true\n\t\tsubCancel()\n\t}\n\n\trequire.True(t, found)\n\n\t_, err = pts[0].Client.ContactRequestAccept(ctx, &protocoltypes.ContactRequestAccept_Request{\n\t\tContactPk: config1.AccountPk,\n\t})\n\n\trequire.NoError(t, err)\n\n\t_, err = pts[0].Client.GroupInfo(ctx, &protocoltypes.GroupInfo_Request{\n\t\tContactPk: config1.AccountPk,\n\t})\n\trequire.NoError(t, err)\n}\n"
  },
  {
    "path": "deactivate_test.go",
    "content": "package weshnet_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\t\"time\"\n\n\tds \"github.com/ipfs/go-datastore\"\n\tdsync \"github.com/ipfs/go-datastore/sync\"\n\tmocknet \"github.com/libp2p/go-libp2p/p2p/net/mock\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"berty.tech/weshnet/v2\"\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n\t\"berty.tech/weshnet/v2/pkg/testutil\"\n\t\"berty.tech/weshnet/v2/pkg/tinder\"\n)\n\nfunc TestReactivateAccountGroup(t *testing.T) {\n\ttestutil.FilterStability(t, testutil.Flappy)\n\n\tctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)\n\tdefer cancel()\n\n\tlogger, cleanup := testutil.Logger(t)\n\tdefer cleanup()\n\n\tmn := mocknet.New()\n\tdefer mn.Close()\n\n\tmsrv := tinder.NewMockDriverServer()\n\n\t// Setup 3 nodes\n\tdsA := dsync.MutexWrap(ds.NewMapDatastore())\n\tnodeA, closeNodeA := weshnet.NewTestingProtocol(ctx, t, &weshnet.TestingOpts{\n\t\tLogger:          logger.Named(\"nodeA\"),\n\t\tMocknet:         mn,\n\t\tDiscoveryServer: msrv,\n\t}, dsA)\n\tdefer closeNodeA()\n\n\tdsB := dsync.MutexWrap(ds.NewMapDatastore())\n\tnodeB, closeNodeB := weshnet.NewTestingProtocol(ctx, t, &weshnet.TestingOpts{\n\t\tLogger:          logger.Named(\"nodeB\"),\n\t\tMocknet:         mn,\n\t\tDiscoveryServer: msrv,\n\t}, dsB)\n\tdefer closeNodeB()\n\n\tdsC := dsync.MutexWrap(ds.NewMapDatastore())\n\tnodeC, closeNodeC := weshnet.NewTestingProtocol(ctx, t, &weshnet.TestingOpts{\n\t\tLogger:          logger.Named(\"nodeC\"),\n\t\tMocknet:         mn,\n\t\tDiscoveryServer: msrv,\n\t}, dsC)\n\tdefer closeNodeC()\n\n\t// make connections\n\terr := mn.LinkAll()\n\trequire.NoError(t, err)\n\n\terr = mn.ConnectAllButSelf()\n\trequire.NoError(t, err)\n\n\t// test communication between nodeA and nodeB\n\tnodes := []*weshnet.TestingProtocol{nodeA, nodeB}\n\taddAsContact(ctx, t, nodes, nodes)\n\tsendMessageToContact(ctx, t, []string{\"pre-deactivate nodeA-nodeB\"}, nodes)\n\n\t// reactivate nodeA account group\n\tnodeACfg, err := nodeA.Client.ServiceGetConfiguration(ctx, &protocoltypes.ServiceGetConfiguration_Request{})\n\trequire.NoError(t, err)\n\trequire.NotNil(t, nodeACfg)\n\n\t_, err = nodeA.Client.DeactivateGroup(ctx, &protocoltypes.DeactivateGroup_Request{\n\t\tGroupPk: nodeACfg.AccountGroupPk,\n\t})\n\trequire.NoError(t, err)\n\n\t_, err = nodeA.Client.ActivateGroup(ctx, &protocoltypes.ActivateGroup_Request{\n\t\tGroupPk: nodeACfg.AccountGroupPk,\n\t})\n\trequire.NoError(t, err)\n\n\t// test communication between nodeA and nodeC\n\tnodes = []*weshnet.TestingProtocol{nodeA, nodeC}\n\n\taddAsContact(ctx, t, nodes, nodes)\n\tsendMessageToContact(ctx, t, []string{\"post reactivate nodeA-nodeC\"}, nodes)\n}\n\nfunc TestRaceReactivateAccountGroup(t *testing.T) {\n\ttestutil.FilterStability(t, testutil.Flappy)\n\n\tctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)\n\tdefer cancel()\n\n\tlogger, cleanup := testutil.Logger(t)\n\tdefer cleanup()\n\n\tmn := mocknet.New()\n\tdefer mn.Close()\n\n\tmsrv := tinder.NewMockDriverServer()\n\n\t// Setup 2 nodes\n\tdsA := dsync.MutexWrap(ds.NewMapDatastore())\n\tnodeA, closeNodeA := weshnet.NewTestingProtocol(ctx, t, &weshnet.TestingOpts{\n\t\tLogger:          logger.Named(\"nodeA\"),\n\t\tMocknet:         mn,\n\t\tDiscoveryServer: msrv,\n\t}, dsA)\n\tdefer closeNodeA()\n\n\tdsB := dsync.MutexWrap(ds.NewMapDatastore())\n\tnodeB, closeNodeB := weshnet.NewTestingProtocol(ctx, t, &weshnet.TestingOpts{\n\t\tLogger:          logger.Named(\"nodeB\"),\n\t\tMocknet:         mn,\n\t\tDiscoveryServer: msrv,\n\t}, dsB)\n\tdefer closeNodeB()\n\n\t// make connections\n\terr := mn.LinkAll()\n\trequire.NoError(t, err)\n\n\terr = mn.ConnectAllButSelf()\n\trequire.NoError(t, err)\n\n\t// reactivate nodeA account group\n\tnodeACfg, err := nodeA.Client.ServiceGetConfiguration(ctx, &protocoltypes.ServiceGetConfiguration_Request{})\n\trequire.NoError(t, err)\n\trequire.NotNil(t, nodeACfg)\n\n\tdeactivateFunc := func() {\n\t\tt.Log(\"DeactivateGroup\")\n\t\t_, err := nodeA.Client.DeactivateGroup(ctx, &protocoltypes.DeactivateGroup_Request{\n\t\t\tGroupPk: nodeACfg.AccountGroupPk,\n\t\t})\n\t\trequire.NoError(t, err)\n\t}\n\n\tactivateFunc := func() {\n\t\tt.Log(\"ActivateGroup\")\n\t\t_, err := nodeA.Client.ActivateGroup(ctx, &protocoltypes.ActivateGroup_Request{\n\t\t\tGroupPk: nodeACfg.AccountGroupPk,\n\t\t})\n\t\trequire.NoError(t, err)\n\t}\n\n\tgo deactivateFunc()\n\ttime.Sleep(1 * time.Millisecond)\n\tgo activateFunc()\n\n\t// test communication between nodeA and nodeB\n\ttime.Sleep(3 * time.Second)\n\tnodes := []*weshnet.TestingProtocol{nodeA, nodeB}\n\tt.Log(\"addAsContact\")\n\taddAsContact(ctx, t, nodes, nodes)\n\tt.Log(\"sendMessageToContact\")\n\tsendMessageToContact(ctx, t, []string{\"nodeA-nodeB\"}, nodes)\n}\n\nfunc TestReactivateContactGroup(t *testing.T) {\n\tctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)\n\tdefer cancel()\n\n\tlogger, cleanup := testutil.Logger(t)\n\tdefer cleanup()\n\n\topts := weshnet.TestingOpts{\n\t\tMocknet:     mocknet.New(),\n\t\tLogger:      logger,\n\t\tConnectFunc: weshnet.ConnectAll,\n\t}\n\n\tnodes, cleanup := weshnet.NewTestingProtocolWithMockedPeers(ctx, t, &opts, nil, 2)\n\tdefer cleanup()\n\n\taddAsContact(ctx, t, nodes, nodes)\n\n\t// send messages before deactivating\n\tsendMessageToContact(ctx, t, []string{\"pre-deactivate\"}, nodes)\n\n\t// get contact group\n\tcontactGroup := getContactGroup(ctx, t, nodes[0], nodes[1])\n\n\t// deactivate contact group\n\t_, err := nodes[0].Client.DeactivateGroup(ctx, &protocoltypes.DeactivateGroup_Request{\n\t\tGroupPk: contactGroup.Group.PublicKey,\n\t})\n\trequire.NoError(t, err)\n\n\t// reactivate group\n\t_, err = nodes[0].Client.ActivateGroup(ctx, &protocoltypes.ActivateGroup_Request{\n\t\tGroupPk: contactGroup.Group.PublicKey,\n\t})\n\trequire.NoError(t, err)\n\n\t// send message after reactivating\n\tsendMessageToContact(ctx, t, []string{\"post-reactivate\"}, nodes)\n}\n\nfunc TestRaceReactivateContactGroup(t *testing.T) {\n\ttestutil.FilterStability(t, testutil.Flappy)\n\n\tctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)\n\tdefer cancel()\n\n\tlogger, cleanup := testutil.Logger(t)\n\tdefer cleanup()\n\n\topts := weshnet.TestingOpts{\n\t\tMocknet:     mocknet.New(),\n\t\tLogger:      logger,\n\t\tConnectFunc: weshnet.ConnectAll,\n\t}\n\n\tnodes, cleanup := weshnet.NewTestingProtocolWithMockedPeers(ctx, t, &opts, nil, 2)\n\tdefer cleanup()\n\n\tt.Log(\"addAsContact\")\n\taddAsContact(ctx, t, nodes, nodes)\n\n\t// send messages before deactivating\n\tt.Log(\"sendMessageToContact\")\n\tsendMessageToContact(ctx, t, []string{\"pre-deactivate\"}, nodes)\n\n\t// get contact group\n\tcontactGroup := getContactGroup(ctx, t, nodes[0], nodes[1])\n\n\t// deactivate contact group\n\tdeactivateFunc := func() {\n\t\tt.Log(\"DeactivateGroup\")\n\t\t_, err := nodes[0].Client.DeactivateGroup(ctx, &protocoltypes.DeactivateGroup_Request{\n\t\t\tGroupPk: contactGroup.Group.PublicKey,\n\t\t})\n\t\trequire.NoError(t, err)\n\t}\n\n\t// reactivate group\n\tactivateFunc := func() {\n\t\tt.Log(\"ActivateGroup\")\n\t\t_, err := nodes[0].Client.ActivateGroup(ctx, &protocoltypes.ActivateGroup_Request{\n\t\t\tGroupPk: contactGroup.Group.PublicKey,\n\t\t})\n\t\trequire.NoError(t, err)\n\t}\n\n\tgo deactivateFunc()\n\ttime.Sleep(1 * time.Millisecond)\n\tgo activateFunc()\n\n\t// send message after reactivating\n\ttime.Sleep(5 * time.Second)\n\tt.Log(\"sendMessageToContact\")\n\tsendMessageToContact(ctx, t, []string{\"post-reactivate\"}, nodes)\n}\n\nfunc TestReactivateMultimemberGroup(t *testing.T) {\n\tctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)\n\tdefer cancel()\n\n\tlogger, cleanup := testutil.Logger(t)\n\tdefer cleanup()\n\n\topts := weshnet.TestingOpts{\n\t\tMocknet:     mocknet.New(),\n\t\tLogger:      logger,\n\t\tConnectFunc: weshnet.ConnectAll,\n\t}\n\n\tnodes, cleanup := weshnet.NewTestingProtocolWithMockedPeers(ctx, t, &opts, nil, 2)\n\tdefer cleanup()\n\n\t// Create MultiMember Group\n\tgroup := weshnet.CreateMultiMemberGroupInstance(ctx, t, nodes[0], nodes[1])\n\n\t// Send message before deactivation\n\tsendMessageOnGroup(ctx, t, nodes, nodes, group.PublicKey, []string{\"pre-deactivate\"})\n\n\t// deactivate multimember group\n\t_, err := nodes[0].Client.DeactivateGroup(ctx, &protocoltypes.DeactivateGroup_Request{\n\t\tGroupPk: group.PublicKey,\n\t})\n\trequire.NoError(t, err)\n\n\t// reactivate group\n\t_, err = nodes[0].Client.ActivateGroup(ctx, &protocoltypes.ActivateGroup_Request{\n\t\tGroupPk: group.PublicKey,\n\t})\n\trequire.NoError(t, err)\n\n\t// Send message after reactivation\n\tsendMessageOnGroup(ctx, t, nodes, nodes, group.PublicKey, []string{\"post-deactivate\"})\n}\n"
  },
  {
    "path": "doc.go",
    "content": "// Package weshnet contains code for integrating the Berty protocol in your project.\n//\n// See https://berty.tech/protocol for more information.\npackage weshnet\n"
  },
  {
    "path": "docs/CONTRIBUTING.md",
    "content": "# Contributing\n\nPlease visit https://github.com/berty/community/blob/master/CONTRIBUTING.md\n"
  },
  {
    "path": "docs/Makefile",
    "content": "##\n## Code gen\n##\n\nVERSION ?= `go run github.com/mdomke/git-semver/v5`\n\nall: generate\n\ngen_src := $(wildcard ../api/*.proto) $(wildcard ../api/*.yaml) Makefile\ngen_sum := gen.sum\n\ngenerate: gen.sum\n$(gen_sum): $(gen_src)\n\t@shasum $(gen_src) | sort -k 2 > $(gen_sum).tmp\n\t@diff -q $(gen_sum).tmp $(gen_sum) || ( \\\n\t  set -xe; \\\n\t  (set -e; GO111MODULE=on go mod download); \\\n\t  docker run \\\n\t    --user=`id -u` \\\n\t    --volume=\"$(PWD)/..:/go/src/berty.tech/berty\" \\\n\t    --volume=\"`go env GOPATH`/pkg/mod:/go/pkg/mod\" \\\n\t    --workdir=\"/go/src/berty.tech/berty/docs\" \\\n\t    --entrypoint=\"sh\" \\\n\t    --rm \\\n\t    bertytech/buf:5 \\\n\t    -xec 'make generate_local' \\\n\t)\n.PHONY: generate\n\nprotoc_opts := -I ../api:`go list -m -mod=mod -f {{.Dir}} github.com/grpc-ecosystem/grpc-gateway`/third_party/googleapis\n\ngenerate_local:\n\tmkdir -p protocol\n\tbuf generate --template ./buf-doc.gen.yaml ../api/protocol/protocoltypes.proto -o protocol\n\t@# replace multiple empty lines with one\n\tcat protocol/api.md.tmp | sed '/^$$/N;/^\\n$$/D' > protocol/api.md\n\trm -f */*.md.tmp\n\tshasum $(gen_src) | sort -k 2 > $(gen_sum).tmp\n\tmv $(gen_sum).tmp $(gen_sum)\n\tmv protocol/api.md apis/protocoltypes.md\n\tmv protocol/protocoltypes.swagger.json apis/\n.PHONY: generate_local\n\nregenerate: gen.clean generate\n.PHONY: regenerate\n\ngen.clean:\n\trm -f gen.sum $(wildcard */*.md.tmp) $(wildcard */*.swagger.json)\n.PHONY: gen.clean\n\nopenapi.prepare: gen.sum\n\tmkdir -p .tmp/openapi\n\tcat ./apis/protocoltypes.swagger.json   | jq '.info.version=\"'$(VERSION)'\"' > .tmp/openapi/protocoltypes.swagger.json\n\tcat .tmp/openapi/*.json | jq .info.version\n.PHONY: openapi.prepare\n\nBUMP_TOKEN ?=\nbump.validate: openapi.prepare\n\t@# gem install bump-cli\n\tbump validate --token=$(BUMP_TOKEN) --doc=6eb1bb1e-c65d-4b73-a8c4-0e545742f6db .tmp/openapi/protocoltypes.swagger.json\n.PHONY: bump.validate\n\nbump.deploy: bump.validate\n\t@# gem install bump-cli\n\tbump deploy --token=$(BUMP_TOKEN) --doc=6eb1bb1e-c65d-4b73-a8c4-0e545742f6db .tmp/openapi/protocoltypes.swagger.json\n.PHONY: bump.deploy\n\nopenapi.clean:\n\trm -rf .tmp/openapi\n.PHONY: openapi.clean\n\nclean: gen.clean openapi.clean\n.PHONY: clean\n\ntidy:\n.PHONY: tidy\n"
  },
  {
    "path": "docs/apis/protocoltypes.md",
    "content": "# Protocol Documentation\n<a name=\"top\"></a>\n\n## Table of Contents\n\n- [protocoltypes.proto](#protocoltypes-proto)\n    - [Account](#weshnet-protocol-v1-Account)\n    - [AccountContactBlocked](#weshnet-protocol-v1-AccountContactBlocked)\n    - [AccountContactRequestDisabled](#weshnet-protocol-v1-AccountContactRequestDisabled)\n    - [AccountContactRequestEnabled](#weshnet-protocol-v1-AccountContactRequestEnabled)\n    - [AccountContactRequestIncomingAccepted](#weshnet-protocol-v1-AccountContactRequestIncomingAccepted)\n    - [AccountContactRequestIncomingDiscarded](#weshnet-protocol-v1-AccountContactRequestIncomingDiscarded)\n    - [AccountContactRequestIncomingReceived](#weshnet-protocol-v1-AccountContactRequestIncomingReceived)\n    - [AccountContactRequestOutgoingEnqueued](#weshnet-protocol-v1-AccountContactRequestOutgoingEnqueued)\n    - [AccountContactRequestOutgoingSent](#weshnet-protocol-v1-AccountContactRequestOutgoingSent)\n    - [AccountContactRequestReferenceReset](#weshnet-protocol-v1-AccountContactRequestReferenceReset)\n    - [AccountContactUnblocked](#weshnet-protocol-v1-AccountContactUnblocked)\n    - [AccountGroupJoined](#weshnet-protocol-v1-AccountGroupJoined)\n    - [AccountGroupLeft](#weshnet-protocol-v1-AccountGroupLeft)\n    - [AccountVerifiedCredentialRegistered](#weshnet-protocol-v1-AccountVerifiedCredentialRegistered)\n    - [ActivateGroup](#weshnet-protocol-v1-ActivateGroup)\n    - [ActivateGroup.Reply](#weshnet-protocol-v1-ActivateGroup-Reply)\n    - [ActivateGroup.Request](#weshnet-protocol-v1-ActivateGroup-Request)\n    - [AppMessageSend](#weshnet-protocol-v1-AppMessageSend)\n    - [AppMessageSend.Reply](#weshnet-protocol-v1-AppMessageSend-Reply)\n    - [AppMessageSend.Request](#weshnet-protocol-v1-AppMessageSend-Request)\n    - [AppMetadataSend](#weshnet-protocol-v1-AppMetadataSend)\n    - [AppMetadataSend.Reply](#weshnet-protocol-v1-AppMetadataSend-Reply)\n    - [AppMetadataSend.Request](#weshnet-protocol-v1-AppMetadataSend-Request)\n    - [ContactAliasKeyAdded](#weshnet-protocol-v1-ContactAliasKeyAdded)\n    - [ContactAliasKeySend](#weshnet-protocol-v1-ContactAliasKeySend)\n    - [ContactAliasKeySend.Reply](#weshnet-protocol-v1-ContactAliasKeySend-Reply)\n    - [ContactAliasKeySend.Request](#weshnet-protocol-v1-ContactAliasKeySend-Request)\n    - [ContactBlock](#weshnet-protocol-v1-ContactBlock)\n    - [ContactBlock.Reply](#weshnet-protocol-v1-ContactBlock-Reply)\n    - [ContactBlock.Request](#weshnet-protocol-v1-ContactBlock-Request)\n    - [ContactRequestAccept](#weshnet-protocol-v1-ContactRequestAccept)\n    - [ContactRequestAccept.Reply](#weshnet-protocol-v1-ContactRequestAccept-Reply)\n    - [ContactRequestAccept.Request](#weshnet-protocol-v1-ContactRequestAccept-Request)\n    - [ContactRequestDisable](#weshnet-protocol-v1-ContactRequestDisable)\n    - [ContactRequestDisable.Reply](#weshnet-protocol-v1-ContactRequestDisable-Reply)\n    - [ContactRequestDisable.Request](#weshnet-protocol-v1-ContactRequestDisable-Request)\n    - [ContactRequestDiscard](#weshnet-protocol-v1-ContactRequestDiscard)\n    - [ContactRequestDiscard.Reply](#weshnet-protocol-v1-ContactRequestDiscard-Reply)\n    - [ContactRequestDiscard.Request](#weshnet-protocol-v1-ContactRequestDiscard-Request)\n    - [ContactRequestEnable](#weshnet-protocol-v1-ContactRequestEnable)\n    - [ContactRequestEnable.Reply](#weshnet-protocol-v1-ContactRequestEnable-Reply)\n    - [ContactRequestEnable.Request](#weshnet-protocol-v1-ContactRequestEnable-Request)\n    - [ContactRequestReference](#weshnet-protocol-v1-ContactRequestReference)\n    - [ContactRequestReference.Reply](#weshnet-protocol-v1-ContactRequestReference-Reply)\n    - [ContactRequestReference.Request](#weshnet-protocol-v1-ContactRequestReference-Request)\n    - [ContactRequestResetReference](#weshnet-protocol-v1-ContactRequestResetReference)\n    - [ContactRequestResetReference.Reply](#weshnet-protocol-v1-ContactRequestResetReference-Reply)\n    - [ContactRequestResetReference.Request](#weshnet-protocol-v1-ContactRequestResetReference-Request)\n    - [ContactRequestSend](#weshnet-protocol-v1-ContactRequestSend)\n    - [ContactRequestSend.Reply](#weshnet-protocol-v1-ContactRequestSend-Reply)\n    - [ContactRequestSend.Request](#weshnet-protocol-v1-ContactRequestSend-Request)\n    - [ContactUnblock](#weshnet-protocol-v1-ContactUnblock)\n    - [ContactUnblock.Reply](#weshnet-protocol-v1-ContactUnblock-Reply)\n    - [ContactUnblock.Request](#weshnet-protocol-v1-ContactUnblock-Request)\n    - [CredentialVerificationServiceCompleteFlow](#weshnet-protocol-v1-CredentialVerificationServiceCompleteFlow)\n    - [CredentialVerificationServiceCompleteFlow.Reply](#weshnet-protocol-v1-CredentialVerificationServiceCompleteFlow-Reply)\n    - [CredentialVerificationServiceCompleteFlow.Request](#weshnet-protocol-v1-CredentialVerificationServiceCompleteFlow-Request)\n    - [CredentialVerificationServiceInitFlow](#weshnet-protocol-v1-CredentialVerificationServiceInitFlow)\n    - [CredentialVerificationServiceInitFlow.Reply](#weshnet-protocol-v1-CredentialVerificationServiceInitFlow-Reply)\n    - [CredentialVerificationServiceInitFlow.Request](#weshnet-protocol-v1-CredentialVerificationServiceInitFlow-Request)\n    - [DeactivateGroup](#weshnet-protocol-v1-DeactivateGroup)\n    - [DeactivateGroup.Reply](#weshnet-protocol-v1-DeactivateGroup-Reply)\n    - [DeactivateGroup.Request](#weshnet-protocol-v1-DeactivateGroup-Request)\n    - [DebugGroup](#weshnet-protocol-v1-DebugGroup)\n    - [DebugGroup.Reply](#weshnet-protocol-v1-DebugGroup-Reply)\n    - [DebugGroup.Request](#weshnet-protocol-v1-DebugGroup-Request)\n    - [DebugInspectGroupStore](#weshnet-protocol-v1-DebugInspectGroupStore)\n    - [DebugInspectGroupStore.Reply](#weshnet-protocol-v1-DebugInspectGroupStore-Reply)\n    - [DebugInspectGroupStore.Request](#weshnet-protocol-v1-DebugInspectGroupStore-Request)\n    - [DebugListGroups](#weshnet-protocol-v1-DebugListGroups)\n    - [DebugListGroups.Reply](#weshnet-protocol-v1-DebugListGroups-Reply)\n    - [DebugListGroups.Request](#weshnet-protocol-v1-DebugListGroups-Request)\n    - [DecodeContact](#weshnet-protocol-v1-DecodeContact)\n    - [DecodeContact.Reply](#weshnet-protocol-v1-DecodeContact-Reply)\n    - [DecodeContact.Request](#weshnet-protocol-v1-DecodeContact-Request)\n    - [DeviceChainKey](#weshnet-protocol-v1-DeviceChainKey)\n    - [EncryptedMessage](#weshnet-protocol-v1-EncryptedMessage)\n    - [EventContext](#weshnet-protocol-v1-EventContext)\n    - [FirstLastCounters](#weshnet-protocol-v1-FirstLastCounters)\n    - [Group](#weshnet-protocol-v1-Group)\n    - [GroupAddAdditionalRendezvousSeed](#weshnet-protocol-v1-GroupAddAdditionalRendezvousSeed)\n    - [GroupDeviceChainKeyAdded](#weshnet-protocol-v1-GroupDeviceChainKeyAdded)\n    - [GroupDeviceStatus](#weshnet-protocol-v1-GroupDeviceStatus)\n    - [GroupDeviceStatus.Reply](#weshnet-protocol-v1-GroupDeviceStatus-Reply)\n    - [GroupDeviceStatus.Reply.PeerConnected](#weshnet-protocol-v1-GroupDeviceStatus-Reply-PeerConnected)\n    - [GroupDeviceStatus.Reply.PeerDisconnected](#weshnet-protocol-v1-GroupDeviceStatus-Reply-PeerDisconnected)\n    - [GroupDeviceStatus.Reply.PeerReconnecting](#weshnet-protocol-v1-GroupDeviceStatus-Reply-PeerReconnecting)\n    - [GroupDeviceStatus.Request](#weshnet-protocol-v1-GroupDeviceStatus-Request)\n    - [GroupEnvelope](#weshnet-protocol-v1-GroupEnvelope)\n    - [GroupHeadsExport](#weshnet-protocol-v1-GroupHeadsExport)\n    - [GroupInfo](#weshnet-protocol-v1-GroupInfo)\n    - [GroupInfo.Reply](#weshnet-protocol-v1-GroupInfo-Reply)\n    - [GroupInfo.Request](#weshnet-protocol-v1-GroupInfo-Request)\n    - [GroupMemberDeviceAdded](#weshnet-protocol-v1-GroupMemberDeviceAdded)\n    - [GroupMessageEvent](#weshnet-protocol-v1-GroupMessageEvent)\n    - [GroupMessageList](#weshnet-protocol-v1-GroupMessageList)\n    - [GroupMessageList.Request](#weshnet-protocol-v1-GroupMessageList-Request)\n    - [GroupMetadata](#weshnet-protocol-v1-GroupMetadata)\n    - [GroupMetadataEvent](#weshnet-protocol-v1-GroupMetadataEvent)\n    - [GroupMetadataList](#weshnet-protocol-v1-GroupMetadataList)\n    - [GroupMetadataList.Request](#weshnet-protocol-v1-GroupMetadataList-Request)\n    - [GroupMetadataPayloadSent](#weshnet-protocol-v1-GroupMetadataPayloadSent)\n    - [GroupRemoveAdditionalRendezvousSeed](#weshnet-protocol-v1-GroupRemoveAdditionalRendezvousSeed)\n    - [GroupReplicating](#weshnet-protocol-v1-GroupReplicating)\n    - [MessageEnvelope](#weshnet-protocol-v1-MessageEnvelope)\n    - [MessageHeaders](#weshnet-protocol-v1-MessageHeaders)\n    - [MessageHeaders.MetadataEntry](#weshnet-protocol-v1-MessageHeaders-MetadataEntry)\n    - [MultiMemberGroupAdminRoleGrant](#weshnet-protocol-v1-MultiMemberGroupAdminRoleGrant)\n    - [MultiMemberGroupAdminRoleGrant.Reply](#weshnet-protocol-v1-MultiMemberGroupAdminRoleGrant-Reply)\n    - [MultiMemberGroupAdminRoleGrant.Request](#weshnet-protocol-v1-MultiMemberGroupAdminRoleGrant-Request)\n    - [MultiMemberGroupAdminRoleGranted](#weshnet-protocol-v1-MultiMemberGroupAdminRoleGranted)\n    - [MultiMemberGroupAliasResolverAdded](#weshnet-protocol-v1-MultiMemberGroupAliasResolverAdded)\n    - [MultiMemberGroupAliasResolverDisclose](#weshnet-protocol-v1-MultiMemberGroupAliasResolverDisclose)\n    - [MultiMemberGroupAliasResolverDisclose.Reply](#weshnet-protocol-v1-MultiMemberGroupAliasResolverDisclose-Reply)\n    - [MultiMemberGroupAliasResolverDisclose.Request](#weshnet-protocol-v1-MultiMemberGroupAliasResolverDisclose-Request)\n    - [MultiMemberGroupCreate](#weshnet-protocol-v1-MultiMemberGroupCreate)\n    - [MultiMemberGroupCreate.Reply](#weshnet-protocol-v1-MultiMemberGroupCreate-Reply)\n    - [MultiMemberGroupCreate.Request](#weshnet-protocol-v1-MultiMemberGroupCreate-Request)\n    - [MultiMemberGroupInitialMemberAnnounced](#weshnet-protocol-v1-MultiMemberGroupInitialMemberAnnounced)\n    - [MultiMemberGroupInvitationCreate](#weshnet-protocol-v1-MultiMemberGroupInvitationCreate)\n    - [MultiMemberGroupInvitationCreate.Reply](#weshnet-protocol-v1-MultiMemberGroupInvitationCreate-Reply)\n    - [MultiMemberGroupInvitationCreate.Request](#weshnet-protocol-v1-MultiMemberGroupInvitationCreate-Request)\n    - [MultiMemberGroupJoin](#weshnet-protocol-v1-MultiMemberGroupJoin)\n    - [MultiMemberGroupJoin.Reply](#weshnet-protocol-v1-MultiMemberGroupJoin-Reply)\n    - [MultiMemberGroupJoin.Request](#weshnet-protocol-v1-MultiMemberGroupJoin-Request)\n    - [MultiMemberGroupLeave](#weshnet-protocol-v1-MultiMemberGroupLeave)\n    - [MultiMemberGroupLeave.Reply](#weshnet-protocol-v1-MultiMemberGroupLeave-Reply)\n    - [MultiMemberGroupLeave.Request](#weshnet-protocol-v1-MultiMemberGroupLeave-Request)\n    - [OrbitDBMessageHeads](#weshnet-protocol-v1-OrbitDBMessageHeads)\n    - [OrbitDBMessageHeads.Box](#weshnet-protocol-v1-OrbitDBMessageHeads-Box)\n    - [OutOfStoreMessage](#weshnet-protocol-v1-OutOfStoreMessage)\n    - [OutOfStoreMessageEnvelope](#weshnet-protocol-v1-OutOfStoreMessageEnvelope)\n    - [OutOfStoreReceive](#weshnet-protocol-v1-OutOfStoreReceive)\n    - [OutOfStoreReceive.Reply](#weshnet-protocol-v1-OutOfStoreReceive-Reply)\n    - [OutOfStoreReceive.Request](#weshnet-protocol-v1-OutOfStoreReceive-Request)\n    - [OutOfStoreSeal](#weshnet-protocol-v1-OutOfStoreSeal)\n    - [OutOfStoreSeal.Reply](#weshnet-protocol-v1-OutOfStoreSeal-Reply)\n    - [OutOfStoreSeal.Request](#weshnet-protocol-v1-OutOfStoreSeal-Request)\n    - [PeerList](#weshnet-protocol-v1-PeerList)\n    - [PeerList.Peer](#weshnet-protocol-v1-PeerList-Peer)\n    - [PeerList.Reply](#weshnet-protocol-v1-PeerList-Reply)\n    - [PeerList.Request](#weshnet-protocol-v1-PeerList-Request)\n    - [PeerList.Route](#weshnet-protocol-v1-PeerList-Route)\n    - [PeerList.Stream](#weshnet-protocol-v1-PeerList-Stream)\n    - [Progress](#weshnet-protocol-v1-Progress)\n    - [ProtocolMetadata](#weshnet-protocol-v1-ProtocolMetadata)\n    - [RefreshContactRequest](#weshnet-protocol-v1-RefreshContactRequest)\n    - [RefreshContactRequest.Peer](#weshnet-protocol-v1-RefreshContactRequest-Peer)\n    - [RefreshContactRequest.Reply](#weshnet-protocol-v1-RefreshContactRequest-Reply)\n    - [RefreshContactRequest.Request](#weshnet-protocol-v1-RefreshContactRequest-Request)\n    - [ReplicationServiceRegisterGroup](#weshnet-protocol-v1-ReplicationServiceRegisterGroup)\n    - [ReplicationServiceRegisterGroup.Reply](#weshnet-protocol-v1-ReplicationServiceRegisterGroup-Reply)\n    - [ReplicationServiceRegisterGroup.Request](#weshnet-protocol-v1-ReplicationServiceRegisterGroup-Request)\n    - [ReplicationServiceReplicateGroup](#weshnet-protocol-v1-ReplicationServiceReplicateGroup)\n    - [ReplicationServiceReplicateGroup.Reply](#weshnet-protocol-v1-ReplicationServiceReplicateGroup-Reply)\n    - [ReplicationServiceReplicateGroup.Request](#weshnet-protocol-v1-ReplicationServiceReplicateGroup-Request)\n    - [ServiceExportData](#weshnet-protocol-v1-ServiceExportData)\n    - [ServiceExportData.Reply](#weshnet-protocol-v1-ServiceExportData-Reply)\n    - [ServiceExportData.Request](#weshnet-protocol-v1-ServiceExportData-Request)\n    - [ServiceGetConfiguration](#weshnet-protocol-v1-ServiceGetConfiguration)\n    - [ServiceGetConfiguration.Reply](#weshnet-protocol-v1-ServiceGetConfiguration-Reply)\n    - [ServiceGetConfiguration.Request](#weshnet-protocol-v1-ServiceGetConfiguration-Request)\n    - [ServiceToken](#weshnet-protocol-v1-ServiceToken)\n    - [ServiceTokenSupportedService](#weshnet-protocol-v1-ServiceTokenSupportedService)\n    - [ShareContact](#weshnet-protocol-v1-ShareContact)\n    - [ShareContact.Reply](#weshnet-protocol-v1-ShareContact-Reply)\n    - [ShareContact.Request](#weshnet-protocol-v1-ShareContact-Request)\n    - [ShareableContact](#weshnet-protocol-v1-ShareableContact)\n    - [SystemInfo](#weshnet-protocol-v1-SystemInfo)\n    - [SystemInfo.OrbitDB](#weshnet-protocol-v1-SystemInfo-OrbitDB)\n    - [SystemInfo.OrbitDB.ReplicationStatus](#weshnet-protocol-v1-SystemInfo-OrbitDB-ReplicationStatus)\n    - [SystemInfo.P2P](#weshnet-protocol-v1-SystemInfo-P2P)\n    - [SystemInfo.Process](#weshnet-protocol-v1-SystemInfo-Process)\n    - [SystemInfo.Reply](#weshnet-protocol-v1-SystemInfo-Reply)\n    - [SystemInfo.Request](#weshnet-protocol-v1-SystemInfo-Request)\n    - [VerifiedCredentialsList](#weshnet-protocol-v1-VerifiedCredentialsList)\n    - [VerifiedCredentialsList.Reply](#weshnet-protocol-v1-VerifiedCredentialsList-Reply)\n    - [VerifiedCredentialsList.Request](#weshnet-protocol-v1-VerifiedCredentialsList-Request)\n  \n    - [ContactState](#weshnet-protocol-v1-ContactState)\n    - [DebugInspectGroupLogType](#weshnet-protocol-v1-DebugInspectGroupLogType)\n    - [Direction](#weshnet-protocol-v1-Direction)\n    - [EventType](#weshnet-protocol-v1-EventType)\n    - [GroupDeviceStatus.Transport](#weshnet-protocol-v1-GroupDeviceStatus-Transport)\n    - [GroupDeviceStatus.Type](#weshnet-protocol-v1-GroupDeviceStatus-Type)\n    - [GroupType](#weshnet-protocol-v1-GroupType)\n    - [PeerList.Feature](#weshnet-protocol-v1-PeerList-Feature)\n    - [ServiceGetConfiguration.SettingState](#weshnet-protocol-v1-ServiceGetConfiguration-SettingState)\n  \n    - [ProtocolService](#weshnet-protocol-v1-ProtocolService)\n  \n- [Scalar Value Types](#scalar-value-types)\n\n<a name=\"protocoltypes-proto\"></a>\n<p align=\"right\"><a href=\"#top\">Top</a></p>\n\n## protocoltypes.proto\n\n<a name=\"weshnet-protocol-v1-Account\"></a>\n\n### Account\nAccount describes all the secrets that identifies an Account\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| group | [Group](#weshnet-protocol-v1-Group) |  | group specifies which group is used to manage the account |\n| account_private_key | [bytes](#bytes) |  | account_private_key, private part is used to signs handshake, signs device, create contacts group keys via ECDH -- public part is used to have a shareable identity |\n| alias_private_key | [bytes](#bytes) |  | alias_private_key, private part is use to derive group members private keys, signs alias proofs, public part can be shared to contacts to prove identity |\n| public_rendezvous_seed | [bytes](#bytes) |  | public_rendezvous_seed, rendezvous seed used for direct communication |\n\n<a name=\"weshnet-protocol-v1-AccountContactBlocked\"></a>\n\n### AccountContactBlocked\nAccountContactBlocked indicates that a contact is blocked\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| device_pk | [bytes](#bytes) |  | device_pk is the device sending the event, signs the message |\n| contact_pk | [bytes](#bytes) |  | contact_pk is the contact blocked |\n\n<a name=\"weshnet-protocol-v1-AccountContactRequestDisabled\"></a>\n\n### AccountContactRequestDisabled\nAccountContactRequestDisabled indicates that the account should not be advertised on a public rendezvous point\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| device_pk | [bytes](#bytes) |  | device_pk is the device sending the event, signs the message |\n\n<a name=\"weshnet-protocol-v1-AccountContactRequestEnabled\"></a>\n\n### AccountContactRequestEnabled\nAccountContactRequestEnabled indicates that the account should be advertised on a public rendezvous point\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| device_pk | [bytes](#bytes) |  | device_pk is the device sending the event, signs the message |\n\n<a name=\"weshnet-protocol-v1-AccountContactRequestIncomingAccepted\"></a>\n\n### AccountContactRequestIncomingAccepted\nThis event should be followed by an AccountGroupJoined event\nThis event should be followed by GroupMemberDeviceAdded and GroupDeviceChainKeyAdded events within the AccountGroup\nAccountContactRequestIncomingAccepted indicates that a contact request has been accepted\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| device_pk | [bytes](#bytes) |  | device_pk is the device sending the event, signs the message |\n| contact_pk | [bytes](#bytes) |  | contact_pk is the contact whom request is accepted |\n| group_pk | [bytes](#bytes) |  | group_pk is the 1to1 group with the requester user |\n\n<a name=\"weshnet-protocol-v1-AccountContactRequestIncomingDiscarded\"></a>\n\n### AccountContactRequestIncomingDiscarded\nAccountContactRequestIncomingDiscarded indicates that a contact request has been refused\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| device_pk | [bytes](#bytes) |  | device_pk is the device sending the event, signs the message |\n| contact_pk | [bytes](#bytes) |  | contact_pk is the contact whom request is refused |\n\n<a name=\"weshnet-protocol-v1-AccountContactRequestIncomingReceived\"></a>\n\n### AccountContactRequestIncomingReceived\nAccountContactRequestIncomingReceived indicates that the account has received a new contact request\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| device_pk | [bytes](#bytes) |  | device_pk is the device sending the account event (which received the contact request), signs the message |\n| contact_pk | [bytes](#bytes) |  | contact_pk is the account sending the request |\n| contact_rendezvous_seed | [bytes](#bytes) |  | TODO: is this necessary? contact_rendezvous_seed is the rendezvous seed of the contact sending the request |\n| contact_metadata | [bytes](#bytes) |  | TODO: is this necessary? contact_metadata is the metadata specific to the app to identify the contact for the request |\n\n<a name=\"weshnet-protocol-v1-AccountContactRequestOutgoingEnqueued\"></a>\n\n### AccountContactRequestOutgoingEnqueued\nThis event should be followed by an AccountGroupJoined event\nThis event should be followed by a GroupMemberDeviceAdded event within the AccountGroup\nThis event should be followed by a GroupDeviceChainKeyAdded event within the AccountGroup\nAccountContactRequestOutgoingEnqueued indicates that the account will attempt to send a contact request when a matching peer is discovered\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| device_pk | [bytes](#bytes) |  | device_pk is the device sending the event, signs the message |\n| group_pk | [bytes](#bytes) |  | group_pk is the 1to1 group with the requested user |\n| contact | [ShareableContact](#weshnet-protocol-v1-ShareableContact) |  | contact is a message describing how to connect to the other account |\n| own_metadata | [bytes](#bytes) |  | own_metadata is the identifying metadata that will be shared to the other account |\n\n<a name=\"weshnet-protocol-v1-AccountContactRequestOutgoingSent\"></a>\n\n### AccountContactRequestOutgoingSent\nAccountContactRequestOutgoingSent indicates that the account has sent a contact request\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| device_pk | [bytes](#bytes) |  | device_pk is the device sending the account event, signs the message |\n| contact_pk | [bytes](#bytes) |  | contact_pk is the contacted account |\n\n<a name=\"weshnet-protocol-v1-AccountContactRequestReferenceReset\"></a>\n\n### AccountContactRequestReferenceReset\nAccountContactRequestReferenceReset indicates that the account should be advertised on different public rendezvous points\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| device_pk | [bytes](#bytes) |  | device_pk is the device sending the event, signs the message |\n| public_rendezvous_seed | [bytes](#bytes) |  | public_rendezvous_seed is the new rendezvous point seed |\n\n<a name=\"weshnet-protocol-v1-AccountContactUnblocked\"></a>\n\n### AccountContactUnblocked\nAccountContactUnblocked indicates that a contact is unblocked\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| device_pk | [bytes](#bytes) |  | device_pk is the device sending the event, signs the message |\n| contact_pk | [bytes](#bytes) |  | contact_pk is the contact unblocked |\n\n<a name=\"weshnet-protocol-v1-AccountGroupJoined\"></a>\n\n### AccountGroupJoined\nAccountGroupJoined indicates that the account is now part of a new group\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| device_pk | [bytes](#bytes) |  | device_pk is the device sending the event, signs the message |\n| group | [Group](#weshnet-protocol-v1-Group) |  | group describe the joined group |\n\n<a name=\"weshnet-protocol-v1-AccountGroupLeft\"></a>\n\n### AccountGroupLeft\nAccountGroupLeft indicates that the account has left a group\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| device_pk | [bytes](#bytes) |  | device_pk is the device sending the event, signs the message |\n| group_pk | [bytes](#bytes) |  | group_pk references the group left |\n\n<a name=\"weshnet-protocol-v1-AccountVerifiedCredentialRegistered\"></a>\n\n### AccountVerifiedCredentialRegistered\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| device_pk | [bytes](#bytes) |  | device_pk is the public key of the device sending the message |\n| signed_identity_public_key | [bytes](#bytes) |  |  |\n| verified_credential | [string](#string) |  |  |\n| registration_date | [int64](#int64) |  |  |\n| expiration_date | [int64](#int64) |  |  |\n| identifier | [string](#string) |  |  |\n| issuer | [string](#string) |  |  |\n\n<a name=\"weshnet-protocol-v1-ActivateGroup\"></a>\n\n### ActivateGroup\n\n<a name=\"weshnet-protocol-v1-ActivateGroup-Reply\"></a>\n\n### ActivateGroup.Reply\n\n<a name=\"weshnet-protocol-v1-ActivateGroup-Request\"></a>\n\n### ActivateGroup.Request\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| group_pk | [bytes](#bytes) |  | group_pk is the identifier of the group |\n| local_only | [bool](#bool) |  | local_only will open the group without enabling network interactions with other members |\n\n<a name=\"weshnet-protocol-v1-AppMessageSend\"></a>\n\n### AppMessageSend\n\n<a name=\"weshnet-protocol-v1-AppMessageSend-Reply\"></a>\n\n### AppMessageSend.Reply\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| cid | [bytes](#bytes) |  |  |\n\n<a name=\"weshnet-protocol-v1-AppMessageSend-Request\"></a>\n\n### AppMessageSend.Request\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| group_pk | [bytes](#bytes) |  | group_pk is the identifier of the group |\n| payload | [bytes](#bytes) |  | payload is the payload to send |\n\n<a name=\"weshnet-protocol-v1-AppMetadataSend\"></a>\n\n### AppMetadataSend\n\n<a name=\"weshnet-protocol-v1-AppMetadataSend-Reply\"></a>\n\n### AppMetadataSend.Reply\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| cid | [bytes](#bytes) |  |  |\n\n<a name=\"weshnet-protocol-v1-AppMetadataSend-Request\"></a>\n\n### AppMetadataSend.Request\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| group_pk | [bytes](#bytes) |  | group_pk is the identifier of the group |\n| payload | [bytes](#bytes) |  | payload is the payload to send |\n\n<a name=\"weshnet-protocol-v1-ContactAliasKeyAdded\"></a>\n\n### ContactAliasKeyAdded\nContactAliasKeyAdded is an event type where ones shares their alias public key\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| device_pk | [bytes](#bytes) |  | device_pk is the device sending the event, signs the message |\n| alias_pk | [bytes](#bytes) |  | alias_pk is the alias key which will be used to verify a contact identity |\n\n<a name=\"weshnet-protocol-v1-ContactAliasKeySend\"></a>\n\n### ContactAliasKeySend\n\n<a name=\"weshnet-protocol-v1-ContactAliasKeySend-Reply\"></a>\n\n### ContactAliasKeySend.Reply\n\n<a name=\"weshnet-protocol-v1-ContactAliasKeySend-Request\"></a>\n\n### ContactAliasKeySend.Request\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| group_pk | [bytes](#bytes) |  | contact_pk is the identifier of the contact to send the alias public key to |\n\n<a name=\"weshnet-protocol-v1-ContactBlock\"></a>\n\n### ContactBlock\n\n<a name=\"weshnet-protocol-v1-ContactBlock-Reply\"></a>\n\n### ContactBlock.Reply\n\n<a name=\"weshnet-protocol-v1-ContactBlock-Request\"></a>\n\n### ContactBlock.Request\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| contact_pk | [bytes](#bytes) |  | contact_pk is the identifier of the contact to block |\n\n<a name=\"weshnet-protocol-v1-ContactRequestAccept\"></a>\n\n### ContactRequestAccept\n\n<a name=\"weshnet-protocol-v1-ContactRequestAccept-Reply\"></a>\n\n### ContactRequestAccept.Reply\n\n<a name=\"weshnet-protocol-v1-ContactRequestAccept-Request\"></a>\n\n### ContactRequestAccept.Request\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| contact_pk | [bytes](#bytes) |  | contact_pk is the identifier of the contact to accept the request from |\n\n<a name=\"weshnet-protocol-v1-ContactRequestDisable\"></a>\n\n### ContactRequestDisable\n\n<a name=\"weshnet-protocol-v1-ContactRequestDisable-Reply\"></a>\n\n### ContactRequestDisable.Reply\n\n<a name=\"weshnet-protocol-v1-ContactRequestDisable-Request\"></a>\n\n### ContactRequestDisable.Request\n\n<a name=\"weshnet-protocol-v1-ContactRequestDiscard\"></a>\n\n### ContactRequestDiscard\n\n<a name=\"weshnet-protocol-v1-ContactRequestDiscard-Reply\"></a>\n\n### ContactRequestDiscard.Reply\n\n<a name=\"weshnet-protocol-v1-ContactRequestDiscard-Request\"></a>\n\n### ContactRequestDiscard.Request\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| contact_pk | [bytes](#bytes) |  | contact_pk is the identifier of the contact to ignore the request from |\n\n<a name=\"weshnet-protocol-v1-ContactRequestEnable\"></a>\n\n### ContactRequestEnable\n\n<a name=\"weshnet-protocol-v1-ContactRequestEnable-Reply\"></a>\n\n### ContactRequestEnable.Reply\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| public_rendezvous_seed | [bytes](#bytes) |  | public_rendezvous_seed is the rendezvous seed used by the current account |\n\n<a name=\"weshnet-protocol-v1-ContactRequestEnable-Request\"></a>\n\n### ContactRequestEnable.Request\n\n<a name=\"weshnet-protocol-v1-ContactRequestReference\"></a>\n\n### ContactRequestReference\n\n<a name=\"weshnet-protocol-v1-ContactRequestReference-Reply\"></a>\n\n### ContactRequestReference.Reply\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| public_rendezvous_seed | [bytes](#bytes) |  | public_rendezvous_seed is the rendezvous seed used by the current account |\n| enabled | [bool](#bool) |  | enabled indicates if incoming contact requests are enabled |\n\n<a name=\"weshnet-protocol-v1-ContactRequestReference-Request\"></a>\n\n### ContactRequestReference.Request\n\n<a name=\"weshnet-protocol-v1-ContactRequestResetReference\"></a>\n\n### ContactRequestResetReference\n\n<a name=\"weshnet-protocol-v1-ContactRequestResetReference-Reply\"></a>\n\n### ContactRequestResetReference.Reply\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| public_rendezvous_seed | [bytes](#bytes) |  | public_rendezvous_seed is the rendezvous seed used by the current account |\n\n<a name=\"weshnet-protocol-v1-ContactRequestResetReference-Request\"></a>\n\n### ContactRequestResetReference.Request\n\n<a name=\"weshnet-protocol-v1-ContactRequestSend\"></a>\n\n### ContactRequestSend\n\n<a name=\"weshnet-protocol-v1-ContactRequestSend-Reply\"></a>\n\n### ContactRequestSend.Reply\n\n<a name=\"weshnet-protocol-v1-ContactRequestSend-Request\"></a>\n\n### ContactRequestSend.Request\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| contact | [ShareableContact](#weshnet-protocol-v1-ShareableContact) |  | contact is a message describing how to connect to the other account |\n| own_metadata | [bytes](#bytes) |  | own_metadata is the identifying metadata that will be shared to the other account |\n\n<a name=\"weshnet-protocol-v1-ContactUnblock\"></a>\n\n### ContactUnblock\n\n<a name=\"weshnet-protocol-v1-ContactUnblock-Reply\"></a>\n\n### ContactUnblock.Reply\n\n<a name=\"weshnet-protocol-v1-ContactUnblock-Request\"></a>\n\n### ContactUnblock.Request\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| contact_pk | [bytes](#bytes) |  | contact_pk is the identifier of the contact to unblock |\n\n<a name=\"weshnet-protocol-v1-CredentialVerificationServiceCompleteFlow\"></a>\n\n### CredentialVerificationServiceCompleteFlow\n\n<a name=\"weshnet-protocol-v1-CredentialVerificationServiceCompleteFlow-Reply\"></a>\n\n### CredentialVerificationServiceCompleteFlow.Reply\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| identifier | [string](#string) |  |  |\n\n<a name=\"weshnet-protocol-v1-CredentialVerificationServiceCompleteFlow-Request\"></a>\n\n### CredentialVerificationServiceCompleteFlow.Request\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| callback_uri | [string](#string) |  |  |\n\n<a name=\"weshnet-protocol-v1-CredentialVerificationServiceInitFlow\"></a>\n\n### CredentialVerificationServiceInitFlow\n\n<a name=\"weshnet-protocol-v1-CredentialVerificationServiceInitFlow-Reply\"></a>\n\n### CredentialVerificationServiceInitFlow.Reply\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| url | [string](#string) |  |  |\n| secure_url | [bool](#bool) |  |  |\n\n<a name=\"weshnet-protocol-v1-CredentialVerificationServiceInitFlow-Request\"></a>\n\n### CredentialVerificationServiceInitFlow.Request\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| service_url | [string](#string) |  |  |\n| public_key | [bytes](#bytes) |  |  |\n| link | [string](#string) |  |  |\n\n<a name=\"weshnet-protocol-v1-DeactivateGroup\"></a>\n\n### DeactivateGroup\n\n<a name=\"weshnet-protocol-v1-DeactivateGroup-Reply\"></a>\n\n### DeactivateGroup.Reply\n\n<a name=\"weshnet-protocol-v1-DeactivateGroup-Request\"></a>\n\n### DeactivateGroup.Request\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| group_pk | [bytes](#bytes) |  | group_pk is the identifier of the group |\n\n<a name=\"weshnet-protocol-v1-DebugGroup\"></a>\n\n### DebugGroup\n\n<a name=\"weshnet-protocol-v1-DebugGroup-Reply\"></a>\n\n### DebugGroup.Reply\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| peer_ids | [string](#string) | repeated | peer_ids is the list of peer ids connected to the same group |\n\n<a name=\"weshnet-protocol-v1-DebugGroup-Request\"></a>\n\n### DebugGroup.Request\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| group_pk | [bytes](#bytes) |  | group_pk is the identifier of the group |\n\n<a name=\"weshnet-protocol-v1-DebugInspectGroupStore\"></a>\n\n### DebugInspectGroupStore\n\n<a name=\"weshnet-protocol-v1-DebugInspectGroupStore-Reply\"></a>\n\n### DebugInspectGroupStore.Reply\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| cid | [bytes](#bytes) |  | cid is the CID of the IPFS log entry |\n| parent_cids | [bytes](#bytes) | repeated | parent_cids is the list of the parent entries |\n| metadata_event_type | [EventType](#weshnet-protocol-v1-EventType) |  | event_type metadata event type if subscribed to metadata events |\n| device_pk | [bytes](#bytes) |  | device_pk is the public key of the device signing the entry |\n| payload | [bytes](#bytes) |  | payload is the un encrypted entry payload if available |\n\n<a name=\"weshnet-protocol-v1-DebugInspectGroupStore-Request\"></a>\n\n### DebugInspectGroupStore.Request\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| group_pk | [bytes](#bytes) |  | group_pk is the identifier of the group |\n| log_type | [DebugInspectGroupLogType](#weshnet-protocol-v1-DebugInspectGroupLogType) |  | log_type is the log to inspect |\n\n<a name=\"weshnet-protocol-v1-DebugListGroups\"></a>\n\n### DebugListGroups\n\n<a name=\"weshnet-protocol-v1-DebugListGroups-Reply\"></a>\n\n### DebugListGroups.Reply\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| group_pk | [bytes](#bytes) |  | group_pk is the public key of the group |\n| group_type | [GroupType](#weshnet-protocol-v1-GroupType) |  | group_type is the type of the group |\n| contact_pk | [bytes](#bytes) |  | contact_pk is the contact public key if appropriate |\n\n<a name=\"weshnet-protocol-v1-DebugListGroups-Request\"></a>\n\n### DebugListGroups.Request\n\n<a name=\"weshnet-protocol-v1-DecodeContact\"></a>\n\n### DecodeContact\n\n<a name=\"weshnet-protocol-v1-DecodeContact-Reply\"></a>\n\n### DecodeContact.Reply\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| contact | [ShareableContact](#weshnet-protocol-v1-ShareableContact) |  | shareable_contact is the decoded shareable contact. |\n\n<a name=\"weshnet-protocol-v1-DecodeContact-Request\"></a>\n\n### DecodeContact.Request\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| encoded_contact | [bytes](#bytes) |  | encoded_contact is the Protobuf encoding of the shareable contact (as returned by ShareContact). |\n\n<a name=\"weshnet-protocol-v1-DeviceChainKey\"></a>\n\n### DeviceChainKey\nDeviceChainKey is a chain key, which will be encrypted for a specific member of the group\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| chain_key | [bytes](#bytes) |  | chain_key is the current value of the chain key of the group device |\n| counter | [uint64](#uint64) |  | counter is the current value of the counter of the group device |\n\n<a name=\"weshnet-protocol-v1-EncryptedMessage\"></a>\n\n### EncryptedMessage\nEncryptedMessage is used in MessageEnvelope and only readable by groups members that joined before the message was sent\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| plaintext | [bytes](#bytes) |  | plaintext is the app layer data |\n| protocol_metadata | [ProtocolMetadata](#weshnet-protocol-v1-ProtocolMetadata) |  | protocol_metadata is protocol layer data |\n\n<a name=\"weshnet-protocol-v1-EventContext\"></a>\n\n### EventContext\nEventContext adds context (its id, its parents and its attachments) to an event\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| id | [bytes](#bytes) |  | id is the CID of the underlying OrbitDB event |\n| parent_ids | [bytes](#bytes) | repeated | id are the the CIDs of the underlying parents of the OrbitDB event |\n| group_pk | [bytes](#bytes) |  | group_pk receiving the event |\n\n<a name=\"weshnet-protocol-v1-FirstLastCounters\"></a>\n\n### FirstLastCounters\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| first | [uint64](#uint64) |  |  |\n| last | [uint64](#uint64) |  |  |\n\n<a name=\"weshnet-protocol-v1-Group\"></a>\n\n### Group\nGroup define a group and is enough to invite someone to it\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| public_key | [bytes](#bytes) |  | public_key is the identifier of the group, it signs the group secret and the initial member of a multi-member group |\n| secret | [bytes](#bytes) |  | secret is the symmetric secret of the group, which is used to encrypt the metadata |\n| secret_sig | [bytes](#bytes) |  | secret_sig is the signature of the secret used to ensure the validity of the group |\n| group_type | [GroupType](#weshnet-protocol-v1-GroupType) |  | group_type specifies the type of the group, used to determine how device chain key is generated |\n| sign_pub | [bytes](#bytes) |  | sign_pub is the signature public key used to verify entries, not required when secret and secret_sig are provided |\n| link_key | [bytes](#bytes) |  | link_key is the secret key used to exchange group updates and links to attachments, useful for replication services |\n| link_key_sig | [bytes](#bytes) |  | link_key_sig is the signature of the link_key using the group private key |\n\n<a name=\"weshnet-protocol-v1-GroupAddAdditionalRendezvousSeed\"></a>\n\n### GroupAddAdditionalRendezvousSeed\nGroupAddAdditionalRendezvousSeed indicates that an additional rendezvous point should be used for data synchronization\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| device_pk | [bytes](#bytes) |  | device_pk is the device sending the event, signs the message, must be the device of an admin of the group |\n| seed | [bytes](#bytes) |  | seed is the additional rendezvous point seed which should be used |\n\n<a name=\"weshnet-protocol-v1-GroupDeviceChainKeyAdded\"></a>\n\n### GroupDeviceChainKeyAdded\nGroupDeviceChainKeyAdded is an event which indicates to a group member a device chain key\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| device_pk | [bytes](#bytes) |  | device_pk is the device sending the event, signs the message |\n| dest_member_pk | [bytes](#bytes) |  | dest_member_pk is the member who should receive the secret |\n| payload | [bytes](#bytes) |  | payload is the serialization of Payload encrypted for the specified member |\n\n<a name=\"weshnet-protocol-v1-GroupDeviceStatus\"></a>\n\n### GroupDeviceStatus\n\n<a name=\"weshnet-protocol-v1-GroupDeviceStatus-Reply\"></a>\n\n### GroupDeviceStatus.Reply\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| type | [GroupDeviceStatus.Type](#weshnet-protocol-v1-GroupDeviceStatus-Type) |  |  |\n| event | [bytes](#bytes) |  |  |\n\n<a name=\"weshnet-protocol-v1-GroupDeviceStatus-Reply-PeerConnected\"></a>\n\n### GroupDeviceStatus.Reply.PeerConnected\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| peer_id | [string](#string) |  |  |\n| device_pk | [bytes](#bytes) |  |  |\n| transports | [GroupDeviceStatus.Transport](#weshnet-protocol-v1-GroupDeviceStatus-Transport) | repeated |  |\n| maddrs | [string](#string) | repeated |  |\n\n<a name=\"weshnet-protocol-v1-GroupDeviceStatus-Reply-PeerDisconnected\"></a>\n\n### GroupDeviceStatus.Reply.PeerDisconnected\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| peer_id | [string](#string) |  |  |\n\n<a name=\"weshnet-protocol-v1-GroupDeviceStatus-Reply-PeerReconnecting\"></a>\n\n### GroupDeviceStatus.Reply.PeerReconnecting\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| peer_id | [string](#string) |  |  |\n\n<a name=\"weshnet-protocol-v1-GroupDeviceStatus-Request\"></a>\n\n### GroupDeviceStatus.Request\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| group_pk | [bytes](#bytes) |  |  |\n\n<a name=\"weshnet-protocol-v1-GroupEnvelope\"></a>\n\n### GroupEnvelope\nGroupEnvelope is a publicly exposed structure containing a group metadata event\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| nonce | [bytes](#bytes) |  | nonce is used to encrypt the message |\n| event | [bytes](#bytes) |  | event is encrypted using a symmetric key shared among group members |\n\n<a name=\"weshnet-protocol-v1-GroupHeadsExport\"></a>\n\n### GroupHeadsExport\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| public_key | [bytes](#bytes) |  | public_key is the identifier of the group, it signs the group secret and the initial member of a multi-member group |\n| sign_pub | [bytes](#bytes) |  | sign_pub is the signature public key used to verify entries |\n| metadata_heads_cids | [bytes](#bytes) | repeated | metadata_heads_cids are the heads of the metadata store that should be restored from an export |\n| messages_heads_cids | [bytes](#bytes) | repeated | messages_heads_cids are the heads of the metadata store that should be restored from an export |\n| link_key | [bytes](#bytes) |  | link_key |\n\n<a name=\"weshnet-protocol-v1-GroupInfo\"></a>\n\n### GroupInfo\n\n<a name=\"weshnet-protocol-v1-GroupInfo-Reply\"></a>\n\n### GroupInfo.Reply\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| group | [Group](#weshnet-protocol-v1-Group) |  | group is the group invitation, containing the group pk and its type |\n| member_pk | [bytes](#bytes) |  | member_pk is the identifier of the current member in the group |\n| device_pk | [bytes](#bytes) |  | device_pk is the identifier of the current device in the group |\n\n<a name=\"weshnet-protocol-v1-GroupInfo-Request\"></a>\n\n### GroupInfo.Request\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| group_pk | [bytes](#bytes) |  | group_pk is the identifier of the group |\n| contact_pk | [bytes](#bytes) |  | contact_pk is the identifier of the contact |\n\n<a name=\"weshnet-protocol-v1-GroupMemberDeviceAdded\"></a>\n\n### GroupMemberDeviceAdded\nGroupMemberDeviceAdded is an event which indicates to a group a new device (and eventually a new member) is joining it\nWhen added on AccountGroup, this event should be followed by appropriate GroupMemberDeviceAdded and GroupDeviceChainKeyAdded events\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| member_pk | [bytes](#bytes) |  | member_pk is the member sending the event |\n| device_pk | [bytes](#bytes) |  | device_pk is the device sending the event, signs the message |\n| member_sig | [bytes](#bytes) |  | member_sig is used to prove the ownership of the member pk\n\nTODO: signature of what ??? ensure it can&#39;t be replayed |\n\n<a name=\"weshnet-protocol-v1-GroupMessageEvent\"></a>\n\n### GroupMessageEvent\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| event_context | [EventContext](#weshnet-protocol-v1-EventContext) |  | event_context contains context information about the event |\n| headers | [MessageHeaders](#weshnet-protocol-v1-MessageHeaders) |  | headers contains headers of the secure message |\n| message | [bytes](#bytes) |  | message contains the secure message payload |\n\n<a name=\"weshnet-protocol-v1-GroupMessageList\"></a>\n\n### GroupMessageList\n\n<a name=\"weshnet-protocol-v1-GroupMessageList-Request\"></a>\n\n### GroupMessageList.Request\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| group_pk | [bytes](#bytes) |  | group_pk is the identifier of the group |\n| since_id | [bytes](#bytes) |  | since is the lower ID bound used to filter events if not set, will return events since the beginning |\n| since_now | [bool](#bool) |  | since_now will list only new event to come since_id must not be set |\n| until_id | [bytes](#bytes) |  | until is the upper ID bound used to filter events if not set, will subscribe to new events to come |\n| until_now | [bool](#bool) |  | until_now will not list new event to come until_id must not be set |\n| reverse_order | [bool](#bool) |  | reverse_order indicates whether the previous events should be returned in reverse chronological order |\n\n<a name=\"weshnet-protocol-v1-GroupMetadata\"></a>\n\n### GroupMetadata\nGroupMetadata is used in GroupEnvelope and only readable by invited group members\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| event_type | [EventType](#weshnet-protocol-v1-EventType) |  | event_type defines which event type is used |\n| payload | [bytes](#bytes) |  | the serialization depends on event_type, event is symmetrically encrypted |\n| sig | [bytes](#bytes) |  | sig is the signature of the payload, it depends on the event_type for the used key |\n| protocol_metadata | [ProtocolMetadata](#weshnet-protocol-v1-ProtocolMetadata) |  | protocol_metadata is protocol layer data |\n\n<a name=\"weshnet-protocol-v1-GroupMetadataEvent\"></a>\n\n### GroupMetadataEvent\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| event_context | [EventContext](#weshnet-protocol-v1-EventContext) |  | event_context contains context information about the event |\n| metadata | [GroupMetadata](#weshnet-protocol-v1-GroupMetadata) |  | metadata contains the newly available metadata |\n| event | [bytes](#bytes) |  | event_clear clear bytes for the event |\n\n<a name=\"weshnet-protocol-v1-GroupMetadataList\"></a>\n\n### GroupMetadataList\n\n<a name=\"weshnet-protocol-v1-GroupMetadataList-Request\"></a>\n\n### GroupMetadataList.Request\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| group_pk | [bytes](#bytes) |  | group_pk is the identifier of the group |\n| since_id | [bytes](#bytes) |  | since is the lower ID bound used to filter events if not set, will return events since the beginning |\n| since_now | [bool](#bool) |  | since_now will list only new event to come since_id must not be set |\n| until_id | [bytes](#bytes) |  | until is the upper ID bound used to filter events if not set, will subscribe to new events to come |\n| until_now | [bool](#bool) |  | until_now will not list new event to come until_id must not be set |\n| reverse_order | [bool](#bool) |  | reverse_order indicates whether the previous events should be returned in reverse chronological order |\n\n<a name=\"weshnet-protocol-v1-GroupMetadataPayloadSent\"></a>\n\n### GroupMetadataPayloadSent\nGroupMetadataPayloadSent is an app defined message, accessible to future group members\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| device_pk | [bytes](#bytes) |  | device_pk is the device sending the event, signs the message |\n| message | [bytes](#bytes) |  | message is the payload |\n\n<a name=\"weshnet-protocol-v1-GroupRemoveAdditionalRendezvousSeed\"></a>\n\n### GroupRemoveAdditionalRendezvousSeed\nGroupRemoveAdditionalRendezvousSeed indicates that a previously added rendezvous point should be removed\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| device_pk | [bytes](#bytes) |  | device_pk is the device sending the event, signs the message, must be the device of an admin of the group |\n| seed | [bytes](#bytes) |  | seed is the additional rendezvous point seed which should be removed |\n\n<a name=\"weshnet-protocol-v1-GroupReplicating\"></a>\n\n### GroupReplicating\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| device_pk | [bytes](#bytes) |  | device_pk is the device sending the event, signs the message |\n| authentication_url | [string](#string) |  | authentication_url indicates which server has been used for authentication |\n| replication_server | [string](#string) |  | replication_server indicates which server will be used for replication |\n\n<a name=\"weshnet-protocol-v1-MessageEnvelope\"></a>\n\n### MessageEnvelope\nMessageEnvelope is a publicly exposed structure containing a group secure message\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| message_headers | [bytes](#bytes) |  | message_headers is an encrypted serialization using a symmetric key of a MessageHeaders message |\n| message | [bytes](#bytes) |  | message is an encrypted message, only readable by group members who previously received the appropriate chain key |\n| nonce | [bytes](#bytes) |  | nonce is a nonce for message headers |\n\n<a name=\"weshnet-protocol-v1-MessageHeaders\"></a>\n\n### MessageHeaders\nMessageHeaders is used in MessageEnvelope and only readable by invited group members\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| counter | [uint64](#uint64) |  | counter is the current counter value for the specified device |\n| device_pk | [bytes](#bytes) |  | device_pk is the public key of the device sending the message |\n| sig | [bytes](#bytes) |  | sig is the signature of the encrypted message using the device&#39;s private key |\n| metadata | [MessageHeaders.MetadataEntry](#weshnet-protocol-v1-MessageHeaders-MetadataEntry) | repeated | metadata allow to pass custom informations |\n\n<a name=\"weshnet-protocol-v1-MessageHeaders-MetadataEntry\"></a>\n\n### MessageHeaders.MetadataEntry\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| key | [string](#string) |  |  |\n| value | [string](#string) |  |  |\n\n<a name=\"weshnet-protocol-v1-MultiMemberGroupAdminRoleGrant\"></a>\n\n### MultiMemberGroupAdminRoleGrant\n\n<a name=\"weshnet-protocol-v1-MultiMemberGroupAdminRoleGrant-Reply\"></a>\n\n### MultiMemberGroupAdminRoleGrant.Reply\n\n<a name=\"weshnet-protocol-v1-MultiMemberGroupAdminRoleGrant-Request\"></a>\n\n### MultiMemberGroupAdminRoleGrant.Request\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| group_pk | [bytes](#bytes) |  | group_pk is the identifier of the group |\n| member_pk | [bytes](#bytes) |  | member_pk is the identifier of the member which will be granted the admin role |\n\n<a name=\"weshnet-protocol-v1-MultiMemberGroupAdminRoleGranted\"></a>\n\n### MultiMemberGroupAdminRoleGranted\nMultiMemberGroupAdminRoleGranted indicates that a group admin allows another group member to act as an admin\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| device_pk | [bytes](#bytes) |  | device_pk is the device sending the event, signs the message, must be the device of an admin of the group |\n| grantee_member_pk | [bytes](#bytes) |  | grantee_member_pk is the member public key of the member granted of the admin role |\n\n<a name=\"weshnet-protocol-v1-MultiMemberGroupAliasResolverAdded\"></a>\n\n### MultiMemberGroupAliasResolverAdded\nMultiMemberGroupAliasResolverAdded indicates that a group member want to disclose their presence in the group to their contacts\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| device_pk | [bytes](#bytes) |  | device_pk is the device sending the event, signs the message |\n| alias_resolver | [bytes](#bytes) |  | alias_resolver allows contact of an account to resolve the real identity behind an alias (Multi-Member Group Member) Generated by both contacts and account independently using: hmac(aliasPK, GroupID) |\n| alias_proof | [bytes](#bytes) |  | alias_proof ensures that the associated alias_resolver has been issued by the right account Generated using aliasSKSig(GroupID) |\n\n<a name=\"weshnet-protocol-v1-MultiMemberGroupAliasResolverDisclose\"></a>\n\n### MultiMemberGroupAliasResolverDisclose\n\n<a name=\"weshnet-protocol-v1-MultiMemberGroupAliasResolverDisclose-Reply\"></a>\n\n### MultiMemberGroupAliasResolverDisclose.Reply\n\n<a name=\"weshnet-protocol-v1-MultiMemberGroupAliasResolverDisclose-Request\"></a>\n\n### MultiMemberGroupAliasResolverDisclose.Request\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| group_pk | [bytes](#bytes) |  | group_pk is the identifier of the group |\n\n<a name=\"weshnet-protocol-v1-MultiMemberGroupCreate\"></a>\n\n### MultiMemberGroupCreate\n\n<a name=\"weshnet-protocol-v1-MultiMemberGroupCreate-Reply\"></a>\n\n### MultiMemberGroupCreate.Reply\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| group_pk | [bytes](#bytes) |  | group_pk is the identifier of the newly created group |\n\n<a name=\"weshnet-protocol-v1-MultiMemberGroupCreate-Request\"></a>\n\n### MultiMemberGroupCreate.Request\n\n<a name=\"weshnet-protocol-v1-MultiMemberGroupInitialMemberAnnounced\"></a>\n\n### MultiMemberGroupInitialMemberAnnounced\nMultiMemberGroupInitialMemberAnnounced indicates that a member is the group creator, this event is signed using the group ID private key\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| member_pk | [bytes](#bytes) |  | member_pk is the public key of the member who is the group creator |\n\n<a name=\"weshnet-protocol-v1-MultiMemberGroupInvitationCreate\"></a>\n\n### MultiMemberGroupInvitationCreate\n\n<a name=\"weshnet-protocol-v1-MultiMemberGroupInvitationCreate-Reply\"></a>\n\n### MultiMemberGroupInvitationCreate.Reply\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| group | [Group](#weshnet-protocol-v1-Group) |  | group is the invitation to the group |\n\n<a name=\"weshnet-protocol-v1-MultiMemberGroupInvitationCreate-Request\"></a>\n\n### MultiMemberGroupInvitationCreate.Request\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| group_pk | [bytes](#bytes) |  | group_pk is the identifier of the group |\n\n<a name=\"weshnet-protocol-v1-MultiMemberGroupJoin\"></a>\n\n### MultiMemberGroupJoin\n\n<a name=\"weshnet-protocol-v1-MultiMemberGroupJoin-Reply\"></a>\n\n### MultiMemberGroupJoin.Reply\n\n<a name=\"weshnet-protocol-v1-MultiMemberGroupJoin-Request\"></a>\n\n### MultiMemberGroupJoin.Request\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| group | [Group](#weshnet-protocol-v1-Group) |  | group is the information of the group to join |\n\n<a name=\"weshnet-protocol-v1-MultiMemberGroupLeave\"></a>\n\n### MultiMemberGroupLeave\n\n<a name=\"weshnet-protocol-v1-MultiMemberGroupLeave-Reply\"></a>\n\n### MultiMemberGroupLeave.Reply\n\n<a name=\"weshnet-protocol-v1-MultiMemberGroupLeave-Request\"></a>\n\n### MultiMemberGroupLeave.Request\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| group_pk | [bytes](#bytes) |  |  |\n\n<a name=\"weshnet-protocol-v1-OrbitDBMessageHeads\"></a>\n\n### OrbitDBMessageHeads\nOrbitDBMessageHeads is the payload sent on orbitdb to share peer&#39;s heads\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| sealed_box | [bytes](#bytes) |  | sealed box should contain encrypted Box |\n| raw_rotation | [bytes](#bytes) |  | current topic used |\n\n<a name=\"weshnet-protocol-v1-OrbitDBMessageHeads-Box\"></a>\n\n### OrbitDBMessageHeads.Box\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| address | [string](#string) |  |  |\n| heads | [bytes](#bytes) |  |  |\n| device_pk | [bytes](#bytes) |  |  |\n| peer_id | [bytes](#bytes) |  |  |\n\n<a name=\"weshnet-protocol-v1-OutOfStoreMessage\"></a>\n\n### OutOfStoreMessage\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| cid | [bytes](#bytes) |  |  |\n| device_pk | [bytes](#bytes) |  |  |\n| counter | [fixed64](#fixed64) |  |  |\n| sig | [bytes](#bytes) |  |  |\n| flags | [fixed32](#fixed32) |  |  |\n| encrypted_payload | [bytes](#bytes) |  |  |\n| nonce | [bytes](#bytes) |  |  |\n\n<a name=\"weshnet-protocol-v1-OutOfStoreMessageEnvelope\"></a>\n\n### OutOfStoreMessageEnvelope\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| nonce | [bytes](#bytes) |  |  |\n| box | [bytes](#bytes) |  |  |\n| group_reference | [bytes](#bytes) |  |  |\n\n<a name=\"weshnet-protocol-v1-OutOfStoreReceive\"></a>\n\n### OutOfStoreReceive\n\n<a name=\"weshnet-protocol-v1-OutOfStoreReceive-Reply\"></a>\n\n### OutOfStoreReceive.Reply\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| message | [OutOfStoreMessage](#weshnet-protocol-v1-OutOfStoreMessage) |  |  |\n| cleartext | [bytes](#bytes) |  |  |\n| group_public_key | [bytes](#bytes) |  |  |\n| already_received | [bool](#bool) |  |  |\n\n<a name=\"weshnet-protocol-v1-OutOfStoreReceive-Request\"></a>\n\n### OutOfStoreReceive.Request\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| payload | [bytes](#bytes) |  |  |\n\n<a name=\"weshnet-protocol-v1-OutOfStoreSeal\"></a>\n\n### OutOfStoreSeal\n\n<a name=\"weshnet-protocol-v1-OutOfStoreSeal-Reply\"></a>\n\n### OutOfStoreSeal.Reply\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| encrypted | [bytes](#bytes) |  |  |\n\n<a name=\"weshnet-protocol-v1-OutOfStoreSeal-Request\"></a>\n\n### OutOfStoreSeal.Request\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| cid | [bytes](#bytes) |  |  |\n| group_public_key | [bytes](#bytes) |  |  |\n\n<a name=\"weshnet-protocol-v1-PeerList\"></a>\n\n### PeerList\n\n<a name=\"weshnet-protocol-v1-PeerList-Peer\"></a>\n\n### PeerList.Peer\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| id | [string](#string) |  | id is the libp2p.PeerID. |\n| routes | [PeerList.Route](#weshnet-protocol-v1-PeerList-Route) | repeated | routes are the list of active and known maddr. |\n| errors | [string](#string) | repeated | errors is a list of errors related to the peer. |\n| features | [PeerList.Feature](#weshnet-protocol-v1-PeerList-Feature) | repeated | Features is a list of available features. |\n| min_latency | [int64](#int64) |  | MinLatency is the minimum latency across all the peer routes. |\n| is_active | [bool](#bool) |  | IsActive is true if at least one of the route is active. |\n| direction | [Direction](#weshnet-protocol-v1-Direction) |  | Direction is the aggregate of all the routes&#39;s direction. |\n\n<a name=\"weshnet-protocol-v1-PeerList-Reply\"></a>\n\n### PeerList.Reply\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| peers | [PeerList.Peer](#weshnet-protocol-v1-PeerList-Peer) | repeated |  |\n\n<a name=\"weshnet-protocol-v1-PeerList-Request\"></a>\n\n### PeerList.Request\n\n<a name=\"weshnet-protocol-v1-PeerList-Route\"></a>\n\n### PeerList.Route\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| is_active | [bool](#bool) |  | IsActive indicates whether the address is currently used or just known. |\n| address | [string](#string) |  | Address is the multiaddress via which we are connected with the peer. |\n| direction | [Direction](#weshnet-protocol-v1-Direction) |  | Direction is which way the connection was established. |\n| latency | [int64](#int64) |  | Latency is the last known round trip time to the peer in ms. |\n| streams | [PeerList.Stream](#weshnet-protocol-v1-PeerList-Stream) | repeated | Streams returns list of streams established with the peer. |\n\n<a name=\"weshnet-protocol-v1-PeerList-Stream\"></a>\n\n### PeerList.Stream\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| id | [string](#string) |  | id is an identifier used to write protocol headers in streams. |\n\n<a name=\"weshnet-protocol-v1-Progress\"></a>\n\n### Progress\nProgress define a generic object that can be used to display a progress bar for long-running actions.\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| state | [string](#string) |  |  |\n| doing | [string](#string) |  |  |\n| progress | [float](#float) |  |  |\n| completed | [uint64](#uint64) |  |  |\n| total | [uint64](#uint64) |  |  |\n| delay | [uint64](#uint64) |  |  |\n\n<a name=\"weshnet-protocol-v1-ProtocolMetadata\"></a>\n\n### ProtocolMetadata\n\n<a name=\"weshnet-protocol-v1-RefreshContactRequest\"></a>\n\n### RefreshContactRequest\n\n<a name=\"weshnet-protocol-v1-RefreshContactRequest-Peer\"></a>\n\n### RefreshContactRequest.Peer\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| id | [string](#string) |  | id is the libp2p.PeerID. |\n| addrs | [string](#string) | repeated | list of peers multiaddrs. |\n\n<a name=\"weshnet-protocol-v1-RefreshContactRequest-Reply\"></a>\n\n### RefreshContactRequest.Reply\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| peers_found | [RefreshContactRequest.Peer](#weshnet-protocol-v1-RefreshContactRequest-Peer) | repeated | peers found and successfully connected. |\n\n<a name=\"weshnet-protocol-v1-RefreshContactRequest-Request\"></a>\n\n### RefreshContactRequest.Request\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| contact_pk | [bytes](#bytes) |  |  |\n| timeout | [int64](#int64) |  | timeout in second |\n\n<a name=\"weshnet-protocol-v1-ReplicationServiceRegisterGroup\"></a>\n\n### ReplicationServiceRegisterGroup\n\n<a name=\"weshnet-protocol-v1-ReplicationServiceRegisterGroup-Reply\"></a>\n\n### ReplicationServiceRegisterGroup.Reply\n\n<a name=\"weshnet-protocol-v1-ReplicationServiceRegisterGroup-Request\"></a>\n\n### ReplicationServiceRegisterGroup.Request\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| group_pk | [bytes](#bytes) |  |  |\n| token | [string](#string) |  |  |\n| authentication_url | [string](#string) |  |  |\n| replication_server | [string](#string) |  |  |\n\n<a name=\"weshnet-protocol-v1-ReplicationServiceReplicateGroup\"></a>\n\n### ReplicationServiceReplicateGroup\n\n<a name=\"weshnet-protocol-v1-ReplicationServiceReplicateGroup-Reply\"></a>\n\n### ReplicationServiceReplicateGroup.Reply\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| ok | [bool](#bool) |  |  |\n\n<a name=\"weshnet-protocol-v1-ReplicationServiceReplicateGroup-Request\"></a>\n\n### ReplicationServiceReplicateGroup.Request\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| group | [Group](#weshnet-protocol-v1-Group) |  |  |\n\n<a name=\"weshnet-protocol-v1-ServiceExportData\"></a>\n\n### ServiceExportData\n\n<a name=\"weshnet-protocol-v1-ServiceExportData-Reply\"></a>\n\n### ServiceExportData.Reply\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| exported_data | [bytes](#bytes) |  |  |\n\n<a name=\"weshnet-protocol-v1-ServiceExportData-Request\"></a>\n\n### ServiceExportData.Request\n\n<a name=\"weshnet-protocol-v1-ServiceGetConfiguration\"></a>\n\n### ServiceGetConfiguration\n\n<a name=\"weshnet-protocol-v1-ServiceGetConfiguration-Reply\"></a>\n\n### ServiceGetConfiguration.Reply\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| account_pk | [bytes](#bytes) |  | account_pk is the public key of the current account |\n| device_pk | [bytes](#bytes) |  | device_pk is the public key of the current device |\n| account_group_pk | [bytes](#bytes) |  | account_group_pk is the public key of the account group |\n| peer_id | [string](#string) |  | peer_id is the peer ID of the current IPFS node |\n| listeners | [string](#string) | repeated | listeners is the list of swarm listening addresses of the current IPFS node |\n| ble_enabled | [ServiceGetConfiguration.SettingState](#weshnet-protocol-v1-ServiceGetConfiguration-SettingState) |  |  |\n| wifi_p2p_enabled | [ServiceGetConfiguration.SettingState](#weshnet-protocol-v1-ServiceGetConfiguration-SettingState) |  | MultiPeerConnectivity for Darwin and Nearby for Android |\n| mdns_enabled | [ServiceGetConfiguration.SettingState](#weshnet-protocol-v1-ServiceGetConfiguration-SettingState) |  |  |\n| relay_enabled | [ServiceGetConfiguration.SettingState](#weshnet-protocol-v1-ServiceGetConfiguration-SettingState) |  |  |\n\n<a name=\"weshnet-protocol-v1-ServiceGetConfiguration-Request\"></a>\n\n### ServiceGetConfiguration.Request\n\n<a name=\"weshnet-protocol-v1-ServiceToken\"></a>\n\n### ServiceToken\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| token | [string](#string) |  |  |\n| authentication_url | [string](#string) |  |  |\n| supported_services | [ServiceTokenSupportedService](#weshnet-protocol-v1-ServiceTokenSupportedService) | repeated |  |\n| expiration | [int64](#int64) |  |  |\n\n<a name=\"weshnet-protocol-v1-ServiceTokenSupportedService\"></a>\n\n### ServiceTokenSupportedService\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| service_type | [string](#string) |  |  |\n| service_endpoint | [string](#string) |  |  |\n\n<a name=\"weshnet-protocol-v1-ShareContact\"></a>\n\n### ShareContact\n\n<a name=\"weshnet-protocol-v1-ShareContact-Reply\"></a>\n\n### ShareContact.Reply\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| encoded_contact | [bytes](#bytes) |  | encoded_contact is the Protobuf encoding of the ShareableContact. You can further encode the bytes for sharing, such as base58 or QR code. |\n\n<a name=\"weshnet-protocol-v1-ShareContact-Request\"></a>\n\n### ShareContact.Request\n\n<a name=\"weshnet-protocol-v1-ShareableContact\"></a>\n\n### ShareableContact\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| pk | [bytes](#bytes) |  | pk is the account to send a contact request to |\n| public_rendezvous_seed | [bytes](#bytes) |  | public_rendezvous_seed is the rendezvous seed used by the account to send a contact request to |\n| metadata | [bytes](#bytes) |  | metadata is the metadata specific to the app to identify the contact for the request |\n\n<a name=\"weshnet-protocol-v1-SystemInfo\"></a>\n\n### SystemInfo\n\n<a name=\"weshnet-protocol-v1-SystemInfo-OrbitDB\"></a>\n\n### SystemInfo.OrbitDB\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| account_metadata | [SystemInfo.OrbitDB.ReplicationStatus](#weshnet-protocol-v1-SystemInfo-OrbitDB-ReplicationStatus) |  |  |\n\n<a name=\"weshnet-protocol-v1-SystemInfo-OrbitDB-ReplicationStatus\"></a>\n\n### SystemInfo.OrbitDB.ReplicationStatus\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| progress | [int64](#int64) |  |  |\n| maximum | [int64](#int64) |  |  |\n| buffered | [int64](#int64) |  |  |\n| queued | [int64](#int64) |  |  |\n\n<a name=\"weshnet-protocol-v1-SystemInfo-P2P\"></a>\n\n### SystemInfo.P2P\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| connected_peers | [int64](#int64) |  |  |\n\n<a name=\"weshnet-protocol-v1-SystemInfo-Process\"></a>\n\n### SystemInfo.Process\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| version | [string](#string) |  |  |\n| vcs_ref | [string](#string) |  |  |\n| uptime_ms | [int64](#int64) |  |  |\n| user_cpu_time_ms | [int64](#int64) |  |  |\n| system_cpu_time_ms | [int64](#int64) |  |  |\n| started_at | [int64](#int64) |  |  |\n| rlimit_cur | [uint64](#uint64) |  |  |\n| num_goroutine | [int64](#int64) |  |  |\n| nofile | [int64](#int64) |  |  |\n| too_many_open_files | [bool](#bool) |  |  |\n| num_cpu | [int64](#int64) |  |  |\n| go_version | [string](#string) |  |  |\n| operating_system | [string](#string) |  |  |\n| host_name | [string](#string) |  |  |\n| arch | [string](#string) |  |  |\n| rlimit_max | [uint64](#uint64) |  |  |\n| pid | [int64](#int64) |  |  |\n| ppid | [int64](#int64) |  |  |\n| priority | [int64](#int64) |  |  |\n| uid | [int64](#int64) |  |  |\n| working_dir | [string](#string) |  |  |\n| system_username | [string](#string) |  |  |\n\n<a name=\"weshnet-protocol-v1-SystemInfo-Reply\"></a>\n\n### SystemInfo.Reply\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| process | [SystemInfo.Process](#weshnet-protocol-v1-SystemInfo-Process) |  |  |\n| p2p | [SystemInfo.P2P](#weshnet-protocol-v1-SystemInfo-P2P) |  |  |\n| orbitdb | [SystemInfo.OrbitDB](#weshnet-protocol-v1-SystemInfo-OrbitDB) |  |  |\n| warns | [string](#string) | repeated |  |\n\n<a name=\"weshnet-protocol-v1-SystemInfo-Request\"></a>\n\n### SystemInfo.Request\n\n<a name=\"weshnet-protocol-v1-VerifiedCredentialsList\"></a>\n\n### VerifiedCredentialsList\n\n<a name=\"weshnet-protocol-v1-VerifiedCredentialsList-Reply\"></a>\n\n### VerifiedCredentialsList.Reply\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| credential | [AccountVerifiedCredentialRegistered](#weshnet-protocol-v1-AccountVerifiedCredentialRegistered) |  |  |\n\n<a name=\"weshnet-protocol-v1-VerifiedCredentialsList-Request\"></a>\n\n### VerifiedCredentialsList.Request\n\n| Field | Type | Label | Description |\n| ----- | ---- | ----- | ----------- |\n| filter_identifier | [string](#string) |  |  |\n| filter_issuer | [string](#string) |  |  |\n| exclude_expired | [bool](#bool) |  |  |\n\n \n\n<a name=\"weshnet-protocol-v1-ContactState\"></a>\n\n### ContactState\n\n| Name | Number | Description |\n| ---- | ------ | ----------- |\n| ContactStateUndefined | 0 |  |\n| ContactStateToRequest | 1 |  |\n| ContactStateReceived | 2 |  |\n| ContactStateAdded | 3 |  |\n| ContactStateRemoved | 4 |  |\n| ContactStateDiscarded | 5 |  |\n| ContactStateBlocked | 6 |  |\n\n<a name=\"weshnet-protocol-v1-DebugInspectGroupLogType\"></a>\n\n### DebugInspectGroupLogType\n\n| Name | Number | Description |\n| ---- | ------ | ----------- |\n| DebugInspectGroupLogTypeUndefined | 0 |  |\n| DebugInspectGroupLogTypeMessage | 1 |  |\n| DebugInspectGroupLogTypeMetadata | 2 |  |\n\n<a name=\"weshnet-protocol-v1-Direction\"></a>\n\n### Direction\n\n| Name | Number | Description |\n| ---- | ------ | ----------- |\n| UnknownDir | 0 |  |\n| InboundDir | 1 |  |\n| OutboundDir | 2 |  |\n| BiDir | 3 |  |\n\n<a name=\"weshnet-protocol-v1-EventType\"></a>\n\n### EventType\n\n| Name | Number | Description |\n| ---- | ------ | ----------- |\n| EventTypeUndefined | 0 | EventTypeUndefined indicates that the value has not been set. Should not happen. |\n| EventTypeGroupMemberDeviceAdded | 1 | EventTypeGroupMemberDeviceAdded indicates the payload includes that a member has added their device to the group |\n| EventTypeGroupDeviceChainKeyAdded | 2 | EventTypeGroupDeviceChainKeyAdded indicates the payload includes that a member has sent their device chain key to another member |\n| EventTypeAccountGroupJoined | 101 | EventTypeAccountGroupJoined indicates the payload includes that the account has joined a group |\n| EventTypeAccountGroupLeft | 102 | EventTypeAccountGroupLeft indicates the payload includes that the account has left a group |\n| EventTypeAccountContactRequestDisabled | 103 | EventTypeAccountContactRequestDisabled indicates the payload includes that the account has disabled incoming contact requests |\n| EventTypeAccountContactRequestEnabled | 104 | EventTypeAccountContactRequestEnabled indicates the payload includes that the account has enabled incoming contact requests |\n| EventTypeAccountContactRequestReferenceReset | 105 | EventTypeAccountContactRequestReferenceReset indicates the payload includes that the account has a new contact request rendezvous seed |\n| EventTypeAccountContactRequestOutgoingEnqueued | 106 | EventTypeAccountContactRequestOutgoingEnqueued indicates the payload includes that the account will attempt to send a new contact request |\n| EventTypeAccountContactRequestOutgoingSent | 107 | EventTypeAccountContactRequestOutgoingSent indicates the payload includes that the account has sent a contact request |\n| EventTypeAccountContactRequestIncomingReceived | 108 | EventTypeAccountContactRequestIncomingReceived indicates the payload includes that the account has received a contact request |\n| EventTypeAccountContactRequestIncomingDiscarded | 109 | EventTypeAccountContactRequestIncomingDiscarded indicates the payload includes that the account has ignored a contact request |\n| EventTypeAccountContactRequestIncomingAccepted | 110 | EventTypeAccountContactRequestIncomingAccepted indicates the payload includes that the account has accepted a contact request |\n| EventTypeAccountContactBlocked | 111 | EventTypeAccountContactBlocked indicates the payload includes that the account has blocked a contact |\n| EventTypeAccountContactUnblocked | 112 | EventTypeAccountContactUnblocked indicates the payload includes that the account has unblocked a contact |\n| EventTypeContactAliasKeyAdded | 201 | EventTypeContactAliasKeyAdded indicates the payload includes that the contact group has received an alias key |\n| EventTypeMultiMemberGroupAliasResolverAdded | 301 | EventTypeMultiMemberGroupAliasResolverAdded indicates the payload includes that a member of the group sent their alias proof |\n| EventTypeMultiMemberGroupInitialMemberAnnounced | 302 | EventTypeMultiMemberGroupInitialMemberAnnounced indicates the payload includes that a member has authenticated themselves as the group owner |\n| EventTypeMultiMemberGroupAdminRoleGranted | 303 | EventTypeMultiMemberGroupAdminRoleGranted indicates the payload includes that an admin of the group granted another member as an admin |\n| EventTypeGroupReplicating | 403 | EventTypeGroupReplicating indicates that the group has been registered for replication on a server |\n| EventTypeAccountVerifiedCredentialRegistered | 500 | EventTypeAccountVerifiedCredentialRegistered |\n| EventTypeGroupMetadataPayloadSent | 1001 | EventTypeGroupMetadataPayloadSent indicates the payload includes an app specific event, unlike messages stored on the message store it is encrypted using a static key |\n\n<a name=\"weshnet-protocol-v1-GroupDeviceStatus-Transport\"></a>\n\n### GroupDeviceStatus.Transport\n\n| Name | Number | Description |\n| ---- | ------ | ----------- |\n| TptUnknown | 0 |  |\n| TptLAN | 1 |  |\n| TptWAN | 2 |  |\n| TptProximity | 3 |  |\n\n<a name=\"weshnet-protocol-v1-GroupDeviceStatus-Type\"></a>\n\n### GroupDeviceStatus.Type\n\n| Name | Number | Description |\n| ---- | ------ | ----------- |\n| TypeUnknown | 0 |  |\n| TypePeerDisconnected | 1 |  |\n| TypePeerConnected | 2 |  |\n| TypePeerReconnecting | 3 |  |\n\n<a name=\"weshnet-protocol-v1-GroupType\"></a>\n\n### GroupType\n\n| Name | Number | Description |\n| ---- | ------ | ----------- |\n| GroupTypeUndefined | 0 | GroupTypeUndefined indicates that the value has not been set. For example, happens if group is replicated. |\n| GroupTypeAccount | 1 | GroupTypeAccount is the group managing an account, available to all its devices. |\n| GroupTypeContact | 2 | GroupTypeContact is the group created between two accounts, available to all their devices. |\n| GroupTypeMultiMember | 3 | GroupTypeMultiMember is a group containing an undefined number of members. |\n\n<a name=\"weshnet-protocol-v1-PeerList-Feature\"></a>\n\n### PeerList.Feature\n\n| Name | Number | Description |\n| ---- | ------ | ----------- |\n| UnknownFeature | 0 |  |\n| WeshFeature | 1 |  |\n| BLEFeature | 2 |  |\n| LocalFeature | 3 |  |\n| TorFeature | 4 |  |\n| QuicFeature | 5 |  |\n\n<a name=\"weshnet-protocol-v1-ServiceGetConfiguration-SettingState\"></a>\n\n### ServiceGetConfiguration.SettingState\n\n| Name | Number | Description |\n| ---- | ------ | ----------- |\n| Unknown | 0 |  |\n| Enabled | 1 |  |\n| Disabled | 2 |  |\n| Unavailable | 3 |  |\n\n \n\n \n\n<a name=\"weshnet-protocol-v1-ProtocolService\"></a>\n\n### ProtocolService\nProtocolService is the top-level API to manage the Wesh protocol service.\nEach active Wesh protocol service is considered as a Wesh device and is associated with a Wesh user.\n\n| Method Name | Request Type | Response Type | Description |\n| ----------- | ------------ | ------------- | ------------|\n| ServiceExportData | [ServiceExportData.Request](#weshnet-protocol-v1-ServiceExportData-Request) | [ServiceExportData.Reply](#weshnet-protocol-v1-ServiceExportData-Reply) stream | ServiceExportData exports the current data of the protocol service |\n| ServiceGetConfiguration | [ServiceGetConfiguration.Request](#weshnet-protocol-v1-ServiceGetConfiguration-Request) | [ServiceGetConfiguration.Reply](#weshnet-protocol-v1-ServiceGetConfiguration-Reply) | ServiceGetConfiguration gets the current configuration of the protocol service |\n| ContactRequestReference | [ContactRequestReference.Request](#weshnet-protocol-v1-ContactRequestReference-Request) | [ContactRequestReference.Reply](#weshnet-protocol-v1-ContactRequestReference-Reply) | ContactRequestReference retrieves the information required to create a reference (ie. included in a shareable link) to the current account |\n| ContactRequestDisable | [ContactRequestDisable.Request](#weshnet-protocol-v1-ContactRequestDisable-Request) | [ContactRequestDisable.Reply](#weshnet-protocol-v1-ContactRequestDisable-Reply) | ContactRequestDisable disables incoming contact requests |\n| ContactRequestEnable | [ContactRequestEnable.Request](#weshnet-protocol-v1-ContactRequestEnable-Request) | [ContactRequestEnable.Reply](#weshnet-protocol-v1-ContactRequestEnable-Reply) | ContactRequestEnable enables incoming contact requests |\n| ContactRequestResetReference | [ContactRequestResetReference.Request](#weshnet-protocol-v1-ContactRequestResetReference-Request) | [ContactRequestResetReference.Reply](#weshnet-protocol-v1-ContactRequestResetReference-Reply) | ContactRequestResetReference changes the contact request reference |\n| ContactRequestSend | [ContactRequestSend.Request](#weshnet-protocol-v1-ContactRequestSend-Request) | [ContactRequestSend.Reply](#weshnet-protocol-v1-ContactRequestSend-Reply) | ContactRequestSend attempt to send a contact request |\n| ContactRequestAccept | [ContactRequestAccept.Request](#weshnet-protocol-v1-ContactRequestAccept-Request) | [ContactRequestAccept.Reply](#weshnet-protocol-v1-ContactRequestAccept-Reply) | ContactRequestAccept accepts a contact request |\n| ContactRequestDiscard | [ContactRequestDiscard.Request](#weshnet-protocol-v1-ContactRequestDiscard-Request) | [ContactRequestDiscard.Reply](#weshnet-protocol-v1-ContactRequestDiscard-Reply) | ContactRequestDiscard ignores a contact request, without informing the other user |\n| ShareContact | [ShareContact.Request](#weshnet-protocol-v1-ShareContact-Request) | [ShareContact.Reply](#weshnet-protocol-v1-ShareContact-Reply) | ShareContact uses ContactRequestReference to get the contact information for the current account and returns the Protobuf encoding of a shareable contact which you can further encode and share. If needed, this will reset the contact request reference and enable contact requests. To decode the result, see DecodeContact. |\n| DecodeContact | [DecodeContact.Request](#weshnet-protocol-v1-DecodeContact-Request) | [DecodeContact.Reply](#weshnet-protocol-v1-DecodeContact-Reply) | DecodeContact decodes the Protobuf encoding of a shareable contact which was returned by ShareContact. |\n| ContactBlock | [ContactBlock.Request](#weshnet-protocol-v1-ContactBlock-Request) | [ContactBlock.Reply](#weshnet-protocol-v1-ContactBlock-Reply) | ContactBlock blocks a contact from sending requests |\n| ContactUnblock | [ContactUnblock.Request](#weshnet-protocol-v1-ContactUnblock-Request) | [ContactUnblock.Reply](#weshnet-protocol-v1-ContactUnblock-Reply) | ContactUnblock unblocks a contact from sending requests |\n| ContactAliasKeySend | [ContactAliasKeySend.Request](#weshnet-protocol-v1-ContactAliasKeySend-Request) | [ContactAliasKeySend.Reply](#weshnet-protocol-v1-ContactAliasKeySend-Reply) | ContactAliasKeySend send an alias key to a contact, the contact will be able to assert that your account is being present on a multi-member group |\n| MultiMemberGroupCreate | [MultiMemberGroupCreate.Request](#weshnet-protocol-v1-MultiMemberGroupCreate-Request) | [MultiMemberGroupCreate.Reply](#weshnet-protocol-v1-MultiMemberGroupCreate-Reply) | MultiMemberGroupCreate creates a new multi-member group |\n| MultiMemberGroupJoin | [MultiMemberGroupJoin.Request](#weshnet-protocol-v1-MultiMemberGroupJoin-Request) | [MultiMemberGroupJoin.Reply](#weshnet-protocol-v1-MultiMemberGroupJoin-Reply) | MultiMemberGroupJoin joins a multi-member group |\n| MultiMemberGroupLeave | [MultiMemberGroupLeave.Request](#weshnet-protocol-v1-MultiMemberGroupLeave-Request) | [MultiMemberGroupLeave.Reply](#weshnet-protocol-v1-MultiMemberGroupLeave-Reply) | MultiMemberGroupLeave leaves a multi-member group |\n| MultiMemberGroupAliasResolverDisclose | [MultiMemberGroupAliasResolverDisclose.Request](#weshnet-protocol-v1-MultiMemberGroupAliasResolverDisclose-Request) | [MultiMemberGroupAliasResolverDisclose.Reply](#weshnet-protocol-v1-MultiMemberGroupAliasResolverDisclose-Reply) | MultiMemberGroupAliasResolverDisclose discloses your alias resolver key |\n| MultiMemberGroupAdminRoleGrant | [MultiMemberGroupAdminRoleGrant.Request](#weshnet-protocol-v1-MultiMemberGroupAdminRoleGrant-Request) | [MultiMemberGroupAdminRoleGrant.Reply](#weshnet-protocol-v1-MultiMemberGroupAdminRoleGrant-Reply) | MultiMemberGroupAdminRoleGrant grants an admin role to a group member |\n| MultiMemberGroupInvitationCreate | [MultiMemberGroupInvitationCreate.Request](#weshnet-protocol-v1-MultiMemberGroupInvitationCreate-Request) | [MultiMemberGroupInvitationCreate.Reply](#weshnet-protocol-v1-MultiMemberGroupInvitationCreate-Reply) | MultiMemberGroupInvitationCreate creates an invitation to a multi-member group |\n| AppMetadataSend | [AppMetadataSend.Request](#weshnet-protocol-v1-AppMetadataSend-Request) | [AppMetadataSend.Reply](#weshnet-protocol-v1-AppMetadataSend-Reply) | AppMetadataSend adds an app event to the metadata store, the message is encrypted using a symmetric key and readable by future group members |\n| AppMessageSend | [AppMessageSend.Request](#weshnet-protocol-v1-AppMessageSend-Request) | [AppMessageSend.Reply](#weshnet-protocol-v1-AppMessageSend-Reply) | AppMessageSend adds an app event to the message store, the message is encrypted using a derived key and readable by current group members |\n| GroupMetadataList | [GroupMetadataList.Request](#weshnet-protocol-v1-GroupMetadataList-Request) | [GroupMetadataEvent](#weshnet-protocol-v1-GroupMetadataEvent) stream | GroupMetadataList replays previous and subscribes to new metadata events from the group |\n| GroupMessageList | [GroupMessageList.Request](#weshnet-protocol-v1-GroupMessageList-Request) | [GroupMessageEvent](#weshnet-protocol-v1-GroupMessageEvent) stream | GroupMessageList replays previous and subscribes to new message events from the group |\n| GroupInfo | [GroupInfo.Request](#weshnet-protocol-v1-GroupInfo-Request) | [GroupInfo.Reply](#weshnet-protocol-v1-GroupInfo-Reply) | GroupInfo retrieves information about a group |\n| ActivateGroup | [ActivateGroup.Request](#weshnet-protocol-v1-ActivateGroup-Request) | [ActivateGroup.Reply](#weshnet-protocol-v1-ActivateGroup-Reply) | ActivateGroup explicitly opens a group |\n| DeactivateGroup | [DeactivateGroup.Request](#weshnet-protocol-v1-DeactivateGroup-Request) | [DeactivateGroup.Reply](#weshnet-protocol-v1-DeactivateGroup-Reply) | DeactivateGroup closes a group |\n| GroupDeviceStatus | [GroupDeviceStatus.Request](#weshnet-protocol-v1-GroupDeviceStatus-Request) | [GroupDeviceStatus.Reply](#weshnet-protocol-v1-GroupDeviceStatus-Reply) stream | GroupDeviceStatus monitor device status |\n| DebugListGroups | [DebugListGroups.Request](#weshnet-protocol-v1-DebugListGroups-Request) | [DebugListGroups.Reply](#weshnet-protocol-v1-DebugListGroups-Reply) stream |  |\n| DebugInspectGroupStore | [DebugInspectGroupStore.Request](#weshnet-protocol-v1-DebugInspectGroupStore-Request) | [DebugInspectGroupStore.Reply](#weshnet-protocol-v1-DebugInspectGroupStore-Reply) stream |  |\n| DebugGroup | [DebugGroup.Request](#weshnet-protocol-v1-DebugGroup-Request) | [DebugGroup.Reply](#weshnet-protocol-v1-DebugGroup-Reply) |  |\n| SystemInfo | [SystemInfo.Request](#weshnet-protocol-v1-SystemInfo-Request) | [SystemInfo.Reply](#weshnet-protocol-v1-SystemInfo-Reply) |  |\n| CredentialVerificationServiceInitFlow | [CredentialVerificationServiceInitFlow.Request](#weshnet-protocol-v1-CredentialVerificationServiceInitFlow-Request) | [CredentialVerificationServiceInitFlow.Reply](#weshnet-protocol-v1-CredentialVerificationServiceInitFlow-Reply) | CredentialVerificationServiceInitFlow Initialize a credential verification flow |\n| CredentialVerificationServiceCompleteFlow | [CredentialVerificationServiceCompleteFlow.Request](#weshnet-protocol-v1-CredentialVerificationServiceCompleteFlow-Request) | [CredentialVerificationServiceCompleteFlow.Reply](#weshnet-protocol-v1-CredentialVerificationServiceCompleteFlow-Reply) | CredentialVerificationServiceCompleteFlow Completes a credential verification flow |\n| VerifiedCredentialsList | [VerifiedCredentialsList.Request](#weshnet-protocol-v1-VerifiedCredentialsList-Request) | [VerifiedCredentialsList.Reply](#weshnet-protocol-v1-VerifiedCredentialsList-Reply) stream | VerifiedCredentialsList Retrieves the list of verified credentials |\n| ReplicationServiceRegisterGroup | [ReplicationServiceRegisterGroup.Request](#weshnet-protocol-v1-ReplicationServiceRegisterGroup-Request) | [ReplicationServiceRegisterGroup.Reply](#weshnet-protocol-v1-ReplicationServiceRegisterGroup-Reply) | ReplicationServiceRegisterGroup Asks a replication service to distribute a group contents |\n| PeerList | [PeerList.Request](#weshnet-protocol-v1-PeerList-Request) | [PeerList.Reply](#weshnet-protocol-v1-PeerList-Reply) | PeerList returns a list of P2P peers |\n| OutOfStoreReceive | [OutOfStoreReceive.Request](#weshnet-protocol-v1-OutOfStoreReceive-Request) | [OutOfStoreReceive.Reply](#weshnet-protocol-v1-OutOfStoreReceive-Reply) | OutOfStoreReceive parses a payload received outside a synchronized store |\n| OutOfStoreSeal | [OutOfStoreSeal.Request](#weshnet-protocol-v1-OutOfStoreSeal-Request) | [OutOfStoreSeal.Reply](#weshnet-protocol-v1-OutOfStoreSeal-Reply) | OutOfStoreSeal creates a payload of a message present in store to be sent outside a synchronized store |\n| RefreshContactRequest | [RefreshContactRequest.Request](#weshnet-protocol-v1-RefreshContactRequest-Request) | [RefreshContactRequest.Reply](#weshnet-protocol-v1-RefreshContactRequest-Reply) | RefreshContactRequest try to refresh the contact request for the given contact |\n\n \n\n## Scalar Value Types\n\n| .proto Type | Notes | C++ | Java | Python | Go | C# | PHP | Ruby |\n| ----------- | ----- | --- | ---- | ------ | -- | -- | --- | ---- |\n| <a name=\"double\" /> double |  | double | double | float | float64 | double | float | Float |\n| <a name=\"float\" /> float |  | float | float | float | float32 | float | float | Float |\n| <a name=\"int32\" /> int32 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) |\n| <a name=\"int64\" /> int64 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead. | int64 | long | int/long | int64 | long | integer/string | Bignum |\n| <a name=\"uint32\" /> uint32 | Uses variable-length encoding. | uint32 | int | int/long | uint32 | uint | integer | Bignum or Fixnum (as required) |\n| <a name=\"uint64\" /> uint64 | Uses variable-length encoding. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum or Fixnum (as required) |\n| <a name=\"sint32\" /> sint32 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) |\n| <a name=\"sint64\" /> sint64 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s. | int64 | long | int/long | int64 | long | integer/string | Bignum |\n| <a name=\"fixed32\" /> fixed32 | Always four bytes. More efficient than uint32 if values are often greater than 2^28. | uint32 | int | int | uint32 | uint | integer | Bignum or Fixnum (as required) |\n| <a name=\"fixed64\" /> fixed64 | Always eight bytes. More efficient than uint64 if values are often greater than 2^56. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum |\n| <a name=\"sfixed32\" /> sfixed32 | Always four bytes. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) |\n| <a name=\"sfixed64\" /> sfixed64 | Always eight bytes. | int64 | long | int/long | int64 | long | integer/string | Bignum |\n| <a name=\"bool\" /> bool |  | bool | boolean | boolean | bool | bool | boolean | TrueClass/FalseClass |\n| <a name=\"string\" /> string | A string must always contain UTF-8 encoded or 7-bit ASCII text. | string | String | str/unicode | string | string | string | String (UTF-8) |\n| <a name=\"bytes\" /> bytes | May contain any arbitrary sequence of bytes. | string | ByteString | str | []byte | ByteString | string | String (ASCII-8BIT) |\n\n"
  },
  {
    "path": "docs/apis/protocoltypes.swagger.json",
    "content": "{\n  \"swagger\": \"2.0\",\n  \"info\": {\n    \"title\": \"protocoltypes.proto\",\n    \"version\": \"version not set\"\n  },\n  \"consumes\": [\n    \"application/json\"\n  ],\n  \"produces\": [\n    \"application/json\"\n  ],\n  \"paths\": {},\n  \"definitions\": {\n    \"GroupDeviceStatusType\": {\n      \"type\": \"string\",\n      \"enum\": [\n        \"TypeUnknown\",\n        \"TypePeerDisconnected\",\n        \"TypePeerConnected\",\n        \"TypePeerReconnecting\"\n      ],\n      \"default\": \"TypeUnknown\"\n    },\n    \"OrbitDBReplicationStatus\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"progress\": {\n          \"type\": \"string\",\n          \"format\": \"int64\"\n        },\n        \"maximum\": {\n          \"type\": \"string\",\n          \"format\": \"int64\"\n        },\n        \"buffered\": {\n          \"type\": \"string\",\n          \"format\": \"int64\"\n        },\n        \"queued\": {\n          \"type\": \"string\",\n          \"format\": \"int64\"\n        }\n      }\n    },\n    \"PeerListFeature\": {\n      \"type\": \"string\",\n      \"enum\": [\n        \"UnknownFeature\",\n        \"WeshFeature\",\n        \"BLEFeature\",\n        \"LocalFeature\",\n        \"TorFeature\",\n        \"QuicFeature\"\n      ],\n      \"default\": \"UnknownFeature\"\n    },\n    \"PeerListRoute\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"is_active\": {\n          \"type\": \"boolean\",\n          \"description\": \"IsActive indicates whether the address is currently used or just known.\"\n        },\n        \"address\": {\n          \"type\": \"string\",\n          \"description\": \"Address is the multiaddress via which we are connected with the peer.\"\n        },\n        \"direction\": {\n          \"$ref\": \"#/definitions/v1Direction\",\n          \"description\": \"Direction is which way the connection was established.\"\n        },\n        \"latency\": {\n          \"type\": \"string\",\n          \"format\": \"int64\",\n          \"description\": \"Latency is the last known round trip time to the peer in ms.\"\n        },\n        \"streams\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"$ref\": \"#/definitions/PeerListStream\"\n          },\n          \"description\": \"Streams returns list of streams established with the peer.\"\n        }\n      }\n    },\n    \"PeerListStream\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"id\": {\n          \"type\": \"string\",\n          \"description\": \"id is an identifier used to write protocol headers in streams.\"\n        }\n      }\n    },\n    \"ServiceGetConfigurationSettingState\": {\n      \"type\": \"string\",\n      \"enum\": [\n        \"Unknown\",\n        \"Enabled\",\n        \"Disabled\",\n        \"Unavailable\"\n      ],\n      \"default\": \"Unknown\"\n    },\n    \"SystemInfoOrbitDB\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"account_metadata\": {\n          \"$ref\": \"#/definitions/OrbitDBReplicationStatus\"\n        }\n      }\n    },\n    \"SystemInfoP2P\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"connected_peers\": {\n          \"type\": \"string\",\n          \"format\": \"int64\"\n        }\n      }\n    },\n    \"SystemInfoProcess\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"version\": {\n          \"type\": \"string\"\n        },\n        \"vcs_ref\": {\n          \"type\": \"string\"\n        },\n        \"uptime_ms\": {\n          \"type\": \"string\",\n          \"format\": \"int64\"\n        },\n        \"user_cpu_time_ms\": {\n          \"type\": \"string\",\n          \"format\": \"int64\"\n        },\n        \"system_cpu_time_ms\": {\n          \"type\": \"string\",\n          \"format\": \"int64\"\n        },\n        \"started_at\": {\n          \"type\": \"string\",\n          \"format\": \"int64\"\n        },\n        \"rlimit_cur\": {\n          \"type\": \"string\",\n          \"format\": \"uint64\"\n        },\n        \"num_goroutine\": {\n          \"type\": \"string\",\n          \"format\": \"int64\"\n        },\n        \"nofile\": {\n          \"type\": \"string\",\n          \"format\": \"int64\"\n        },\n        \"too_many_open_files\": {\n          \"type\": \"boolean\"\n        },\n        \"num_cpu\": {\n          \"type\": \"string\",\n          \"format\": \"int64\"\n        },\n        \"go_version\": {\n          \"type\": \"string\"\n        },\n        \"operating_system\": {\n          \"type\": \"string\"\n        },\n        \"host_name\": {\n          \"type\": \"string\"\n        },\n        \"arch\": {\n          \"type\": \"string\"\n        },\n        \"rlimit_max\": {\n          \"type\": \"string\",\n          \"format\": \"uint64\"\n        },\n        \"pid\": {\n          \"type\": \"string\",\n          \"format\": \"int64\"\n        },\n        \"ppid\": {\n          \"type\": \"string\",\n          \"format\": \"int64\"\n        },\n        \"priority\": {\n          \"type\": \"string\",\n          \"format\": \"int64\"\n        },\n        \"uid\": {\n          \"type\": \"string\",\n          \"format\": \"int64\"\n        },\n        \"working_dir\": {\n          \"type\": \"string\"\n        },\n        \"system_username\": {\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"protobufAny\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"type_url\": {\n          \"type\": \"string\"\n        },\n        \"value\": {\n          \"type\": \"string\",\n          \"format\": \"byte\"\n        }\n      }\n    },\n    \"runtimeError\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"error\": {\n          \"type\": \"string\"\n        },\n        \"code\": {\n          \"type\": \"integer\",\n          \"format\": \"int32\"\n        },\n        \"message\": {\n          \"type\": \"string\"\n        },\n        \"details\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"$ref\": \"#/definitions/protobufAny\"\n          }\n        }\n      }\n    },\n    \"runtimeStreamError\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"grpc_code\": {\n          \"type\": \"integer\",\n          \"format\": \"int32\"\n        },\n        \"http_code\": {\n          \"type\": \"integer\",\n          \"format\": \"int32\"\n        },\n        \"message\": {\n          \"type\": \"string\"\n        },\n        \"http_status\": {\n          \"type\": \"string\"\n        },\n        \"details\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"$ref\": \"#/definitions/protobufAny\"\n          }\n        }\n      }\n    },\n    \"v1AccountVerifiedCredentialRegistered\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"device_pk\": {\n          \"type\": \"string\",\n          \"format\": \"byte\",\n          \"title\": \"device_pk is the public key of the device sending the message\"\n        },\n        \"signed_identity_public_key\": {\n          \"type\": \"string\",\n          \"format\": \"byte\"\n        },\n        \"verified_credential\": {\n          \"type\": \"string\"\n        },\n        \"registration_date\": {\n          \"type\": \"string\",\n          \"format\": \"int64\"\n        },\n        \"expiration_date\": {\n          \"type\": \"string\",\n          \"format\": \"int64\"\n        },\n        \"identifier\": {\n          \"type\": \"string\"\n        },\n        \"issuer\": {\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"v1ActivateGroupReply\": {\n      \"type\": \"object\"\n    },\n    \"v1AppMessageSendReply\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"cid\": {\n          \"type\": \"string\",\n          \"format\": \"byte\"\n        }\n      }\n    },\n    \"v1AppMetadataSendReply\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"cid\": {\n          \"type\": \"string\",\n          \"format\": \"byte\"\n        }\n      }\n    },\n    \"v1ContactAliasKeySendReply\": {\n      \"type\": \"object\"\n    },\n    \"v1ContactBlockReply\": {\n      \"type\": \"object\"\n    },\n    \"v1ContactRequestAcceptReply\": {\n      \"type\": \"object\"\n    },\n    \"v1ContactRequestDisableReply\": {\n      \"type\": \"object\"\n    },\n    \"v1ContactRequestDiscardReply\": {\n      \"type\": \"object\"\n    },\n    \"v1ContactRequestEnableReply\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"public_rendezvous_seed\": {\n          \"type\": \"string\",\n          \"format\": \"byte\",\n          \"title\": \"public_rendezvous_seed is the rendezvous seed used by the current account\"\n        }\n      }\n    },\n    \"v1ContactRequestReferenceReply\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"public_rendezvous_seed\": {\n          \"type\": \"string\",\n          \"format\": \"byte\",\n          \"title\": \"public_rendezvous_seed is the rendezvous seed used by the current account\"\n        },\n        \"enabled\": {\n          \"type\": \"boolean\",\n          \"title\": \"enabled indicates if incoming contact requests are enabled\"\n        }\n      }\n    },\n    \"v1ContactRequestResetReferenceReply\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"public_rendezvous_seed\": {\n          \"type\": \"string\",\n          \"format\": \"byte\",\n          \"title\": \"public_rendezvous_seed is the rendezvous seed used by the current account\"\n        }\n      }\n    },\n    \"v1ContactRequestSendReply\": {\n      \"type\": \"object\"\n    },\n    \"v1ContactUnblockReply\": {\n      \"type\": \"object\"\n    },\n    \"v1CredentialVerificationServiceCompleteFlowReply\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"identifier\": {\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"v1CredentialVerificationServiceInitFlowReply\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"url\": {\n          \"type\": \"string\"\n        },\n        \"secure_url\": {\n          \"type\": \"boolean\"\n        }\n      }\n    },\n    \"v1DeactivateGroupReply\": {\n      \"type\": \"object\"\n    },\n    \"v1DebugGroupReply\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"peer_ids\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\"\n          },\n          \"title\": \"peer_ids is the list of peer ids connected to the same group\"\n        }\n      }\n    },\n    \"v1DebugInspectGroupLogType\": {\n      \"type\": \"string\",\n      \"enum\": [\n        \"DebugInspectGroupLogTypeUndefined\",\n        \"DebugInspectGroupLogTypeMessage\",\n        \"DebugInspectGroupLogTypeMetadata\"\n      ],\n      \"default\": \"DebugInspectGroupLogTypeUndefined\"\n    },\n    \"v1DebugInspectGroupStoreReply\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"cid\": {\n          \"type\": \"string\",\n          \"format\": \"byte\",\n          \"title\": \"cid is the CID of the IPFS log entry\"\n        },\n        \"parent_cids\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\",\n            \"format\": \"byte\"\n          },\n          \"title\": \"parent_cids is the list of the parent entries\"\n        },\n        \"metadata_event_type\": {\n          \"$ref\": \"#/definitions/v1EventType\",\n          \"title\": \"event_type metadata event type if subscribed to metadata events\"\n        },\n        \"device_pk\": {\n          \"type\": \"string\",\n          \"format\": \"byte\",\n          \"title\": \"device_pk is the public key of the device signing the entry\"\n        },\n        \"payload\": {\n          \"type\": \"string\",\n          \"format\": \"byte\",\n          \"title\": \"payload is the un encrypted entry payload if available\"\n        }\n      }\n    },\n    \"v1DebugListGroupsReply\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"group_pk\": {\n          \"type\": \"string\",\n          \"format\": \"byte\",\n          \"title\": \"group_pk is the public key of the group\"\n        },\n        \"group_type\": {\n          \"$ref\": \"#/definitions/v1GroupType\",\n          \"title\": \"group_type is the type of the group\"\n        },\n        \"contact_pk\": {\n          \"type\": \"string\",\n          \"format\": \"byte\",\n          \"title\": \"contact_pk is the contact public key if appropriate\"\n        }\n      }\n    },\n    \"v1DecodeContactReply\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"contact\": {\n          \"$ref\": \"#/definitions/v1ShareableContact\",\n          \"description\": \"shareable_contact is the decoded shareable contact.\"\n        }\n      }\n    },\n    \"v1Direction\": {\n      \"type\": \"string\",\n      \"enum\": [\n        \"UnknownDir\",\n        \"InboundDir\",\n        \"OutboundDir\",\n        \"BiDir\"\n      ],\n      \"default\": \"UnknownDir\"\n    },\n    \"v1EventContext\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"id\": {\n          \"type\": \"string\",\n          \"format\": \"byte\",\n          \"title\": \"id is the CID of the underlying OrbitDB event\"\n        },\n        \"parent_ids\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\",\n            \"format\": \"byte\"\n          },\n          \"title\": \"id are the the CIDs of the underlying parents of the OrbitDB event\"\n        },\n        \"group_pk\": {\n          \"type\": \"string\",\n          \"format\": \"byte\",\n          \"title\": \"group_pk receiving the event\"\n        }\n      },\n      \"title\": \"EventContext adds context (its id, its parents and its attachments) to an event\"\n    },\n    \"v1EventType\": {\n      \"type\": \"string\",\n      \"enum\": [\n        \"EventTypeUndefined\",\n        \"EventTypeGroupMemberDeviceAdded\",\n        \"EventTypeGroupDeviceChainKeyAdded\",\n        \"EventTypeAccountGroupJoined\",\n        \"EventTypeAccountGroupLeft\",\n        \"EventTypeAccountContactRequestDisabled\",\n        \"EventTypeAccountContactRequestEnabled\",\n        \"EventTypeAccountContactRequestReferenceReset\",\n        \"EventTypeAccountContactRequestOutgoingEnqueued\",\n        \"EventTypeAccountContactRequestOutgoingSent\",\n        \"EventTypeAccountContactRequestIncomingReceived\",\n        \"EventTypeAccountContactRequestIncomingDiscarded\",\n        \"EventTypeAccountContactRequestIncomingAccepted\",\n        \"EventTypeAccountContactBlocked\",\n        \"EventTypeAccountContactUnblocked\",\n        \"EventTypeContactAliasKeyAdded\",\n        \"EventTypeMultiMemberGroupAliasResolverAdded\",\n        \"EventTypeMultiMemberGroupInitialMemberAnnounced\",\n        \"EventTypeMultiMemberGroupAdminRoleGranted\",\n        \"EventTypeGroupReplicating\",\n        \"EventTypeAccountVerifiedCredentialRegistered\",\n        \"EventTypeGroupMetadataPayloadSent\"\n      ],\n      \"default\": \"EventTypeUndefined\",\n      \"title\": \"- EventTypeUndefined: EventTypeUndefined indicates that the value has not been set. Should not happen.\\n - EventTypeGroupMemberDeviceAdded: EventTypeGroupMemberDeviceAdded indicates the payload includes that a member has added their device to the group\\n - EventTypeGroupDeviceChainKeyAdded: EventTypeGroupDeviceChainKeyAdded indicates the payload includes that a member has sent their device chain key to another member\\n - EventTypeAccountGroupJoined: EventTypeAccountGroupJoined indicates the payload includes that the account has joined a group\\n - EventTypeAccountGroupLeft: EventTypeAccountGroupLeft indicates the payload includes that the account has left a group\\n - EventTypeAccountContactRequestDisabled: EventTypeAccountContactRequestDisabled indicates the payload includes that the account has disabled incoming contact requests\\n - EventTypeAccountContactRequestEnabled: EventTypeAccountContactRequestEnabled indicates the payload includes that the account has enabled incoming contact requests\\n - EventTypeAccountContactRequestReferenceReset: EventTypeAccountContactRequestReferenceReset indicates the payload includes that the account has a new contact request rendezvous seed\\n - EventTypeAccountContactRequestOutgoingEnqueued: EventTypeAccountContactRequestOutgoingEnqueued indicates the payload includes that the account will attempt to send a new contact request\\n - EventTypeAccountContactRequestOutgoingSent: EventTypeAccountContactRequestOutgoingSent indicates the payload includes that the account has sent a contact request\\n - EventTypeAccountContactRequestIncomingReceived: EventTypeAccountContactRequestIncomingReceived indicates the payload includes that the account has received a contact request\\n - EventTypeAccountContactRequestIncomingDiscarded: EventTypeAccountContactRequestIncomingDiscarded indicates the payload includes that the account has ignored a contact request\\n - EventTypeAccountContactRequestIncomingAccepted: EventTypeAccountContactRequestIncomingAccepted indicates the payload includes that the account has accepted a contact request\\n - EventTypeAccountContactBlocked: EventTypeAccountContactBlocked indicates the payload includes that the account has blocked a contact\\n - EventTypeAccountContactUnblocked: EventTypeAccountContactUnblocked indicates the payload includes that the account has unblocked a contact\\n - EventTypeContactAliasKeyAdded: EventTypeContactAliasKeyAdded indicates the payload includes that the contact group has received an alias key\\n - EventTypeMultiMemberGroupAliasResolverAdded: EventTypeMultiMemberGroupAliasResolverAdded indicates the payload includes that a member of the group sent their alias proof\\n - EventTypeMultiMemberGroupInitialMemberAnnounced: EventTypeMultiMemberGroupInitialMemberAnnounced indicates the payload includes that a member has authenticated themselves as the group owner\\n - EventTypeMultiMemberGroupAdminRoleGranted: EventTypeMultiMemberGroupAdminRoleGranted indicates the payload includes that an admin of the group granted another member as an admin\\n - EventTypeGroupReplicating: EventTypeGroupReplicating indicates that the group has been registered for replication on a server\\n - EventTypeAccountVerifiedCredentialRegistered: EventTypeAccountVerifiedCredentialRegistered\\n - EventTypeGroupMetadataPayloadSent: EventTypeGroupMetadataPayloadSent indicates the payload includes an app specific event, unlike messages stored on the message store it is encrypted using a static key\"\n    },\n    \"v1Group\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"public_key\": {\n          \"type\": \"string\",\n          \"format\": \"byte\",\n          \"title\": \"public_key is the identifier of the group, it signs the group secret and the initial member of a multi-member group\"\n        },\n        \"secret\": {\n          \"type\": \"string\",\n          \"format\": \"byte\",\n          \"title\": \"secret is the symmetric secret of the group, which is used to encrypt the metadata\"\n        },\n        \"secret_sig\": {\n          \"type\": \"string\",\n          \"format\": \"byte\",\n          \"title\": \"secret_sig is the signature of the secret used to ensure the validity of the group\"\n        },\n        \"group_type\": {\n          \"$ref\": \"#/definitions/v1GroupType\",\n          \"title\": \"group_type specifies the type of the group, used to determine how device chain key is generated\"\n        },\n        \"sign_pub\": {\n          \"type\": \"string\",\n          \"format\": \"byte\",\n          \"title\": \"sign_pub is the signature public key used to verify entries, not required when secret and secret_sig are provided\"\n        },\n        \"link_key\": {\n          \"type\": \"string\",\n          \"format\": \"byte\",\n          \"title\": \"link_key is the secret key used to exchange group updates and links to attachments, useful for replication services\"\n        },\n        \"link_key_sig\": {\n          \"type\": \"string\",\n          \"format\": \"byte\",\n          \"title\": \"link_key_sig is the signature of the link_key using the group private key\"\n        }\n      },\n      \"title\": \"Group define a group and is enough to invite someone to it\"\n    },\n    \"v1GroupDeviceStatusReply\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"type\": {\n          \"$ref\": \"#/definitions/GroupDeviceStatusType\"\n        },\n        \"event\": {\n          \"type\": \"string\",\n          \"format\": \"byte\"\n        }\n      }\n    },\n    \"v1GroupInfoReply\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"group\": {\n          \"$ref\": \"#/definitions/v1Group\",\n          \"title\": \"group is the group invitation, containing the group pk and its type\"\n        },\n        \"member_pk\": {\n          \"type\": \"string\",\n          \"format\": \"byte\",\n          \"title\": \"member_pk is the identifier of the current member in the group\"\n        },\n        \"device_pk\": {\n          \"type\": \"string\",\n          \"format\": \"byte\",\n          \"title\": \"device_pk is the identifier of the current device in the group\"\n        }\n      }\n    },\n    \"v1GroupMessageEvent\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"event_context\": {\n          \"$ref\": \"#/definitions/v1EventContext\",\n          \"title\": \"event_context contains context information about the event\"\n        },\n        \"headers\": {\n          \"$ref\": \"#/definitions/v1MessageHeaders\",\n          \"title\": \"headers contains headers of the secure message\"\n        },\n        \"message\": {\n          \"type\": \"string\",\n          \"format\": \"byte\",\n          \"title\": \"message contains the secure message payload\"\n        }\n      }\n    },\n    \"v1GroupMetadata\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"event_type\": {\n          \"$ref\": \"#/definitions/v1EventType\",\n          \"title\": \"event_type defines which event type is used\"\n        },\n        \"payload\": {\n          \"type\": \"string\",\n          \"format\": \"byte\",\n          \"title\": \"the serialization depends on event_type, event is symmetrically encrypted\"\n        },\n        \"sig\": {\n          \"type\": \"string\",\n          \"format\": \"byte\",\n          \"title\": \"sig is the signature of the payload, it depends on the event_type for the used key\"\n        },\n        \"protocol_metadata\": {\n          \"$ref\": \"#/definitions/v1ProtocolMetadata\",\n          \"title\": \"protocol_metadata is protocol layer data\"\n        }\n      },\n      \"title\": \"GroupMetadata is used in GroupEnvelope and only readable by invited group members\"\n    },\n    \"v1GroupMetadataEvent\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"event_context\": {\n          \"$ref\": \"#/definitions/v1EventContext\",\n          \"title\": \"event_context contains context information about the event\"\n        },\n        \"metadata\": {\n          \"$ref\": \"#/definitions/v1GroupMetadata\",\n          \"title\": \"metadata contains the newly available metadata\"\n        },\n        \"event\": {\n          \"type\": \"string\",\n          \"format\": \"byte\",\n          \"title\": \"event_clear clear bytes for the event\"\n        }\n      }\n    },\n    \"v1GroupType\": {\n      \"type\": \"string\",\n      \"enum\": [\n        \"GroupTypeUndefined\",\n        \"GroupTypeAccount\",\n        \"GroupTypeContact\",\n        \"GroupTypeMultiMember\"\n      ],\n      \"default\": \"GroupTypeUndefined\",\n      \"description\": \" - GroupTypeUndefined: GroupTypeUndefined indicates that the value has not been set. For example, happens if group is replicated.\\n - GroupTypeAccount: GroupTypeAccount is the group managing an account, available to all its devices.\\n - GroupTypeContact: GroupTypeContact is the group created between two accounts, available to all their devices.\\n - GroupTypeMultiMember: GroupTypeMultiMember is a group containing an undefined number of members.\"\n    },\n    \"v1MessageHeaders\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"counter\": {\n          \"type\": \"string\",\n          \"format\": \"uint64\",\n          \"title\": \"counter is the current counter value for the specified device\"\n        },\n        \"device_pk\": {\n          \"type\": \"string\",\n          \"format\": \"byte\",\n          \"title\": \"device_pk is the public key of the device sending the message\"\n        },\n        \"sig\": {\n          \"type\": \"string\",\n          \"format\": \"byte\",\n          \"title\": \"sig is the signature of the encrypted message using the device's private key\"\n        },\n        \"metadata\": {\n          \"type\": \"object\",\n          \"additionalProperties\": {\n            \"type\": \"string\"\n          },\n          \"title\": \"metadata allow to pass custom informations\"\n        }\n      },\n      \"title\": \"MessageHeaders is used in MessageEnvelope and only readable by invited group members\"\n    },\n    \"v1MultiMemberGroupAdminRoleGrantReply\": {\n      \"type\": \"object\"\n    },\n    \"v1MultiMemberGroupAliasResolverDiscloseReply\": {\n      \"type\": \"object\"\n    },\n    \"v1MultiMemberGroupCreateReply\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"group_pk\": {\n          \"type\": \"string\",\n          \"format\": \"byte\",\n          \"title\": \"group_pk is the identifier of the newly created group\"\n        }\n      }\n    },\n    \"v1MultiMemberGroupInvitationCreateReply\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"group\": {\n          \"$ref\": \"#/definitions/v1Group\",\n          \"title\": \"group is the invitation to the group\"\n        }\n      }\n    },\n    \"v1MultiMemberGroupJoinReply\": {\n      \"type\": \"object\"\n    },\n    \"v1MultiMemberGroupLeaveReply\": {\n      \"type\": \"object\"\n    },\n    \"v1OutOfStoreMessage\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"cid\": {\n          \"type\": \"string\",\n          \"format\": \"byte\"\n        },\n        \"device_pk\": {\n          \"type\": \"string\",\n          \"format\": \"byte\"\n        },\n        \"counter\": {\n          \"type\": \"string\",\n          \"format\": \"uint64\"\n        },\n        \"sig\": {\n          \"type\": \"string\",\n          \"format\": \"byte\"\n        },\n        \"flags\": {\n          \"type\": \"integer\",\n          \"format\": \"int64\"\n        },\n        \"encrypted_payload\": {\n          \"type\": \"string\",\n          \"format\": \"byte\"\n        },\n        \"nonce\": {\n          \"type\": \"string\",\n          \"format\": \"byte\"\n        }\n      }\n    },\n    \"v1OutOfStoreReceiveReply\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"message\": {\n          \"$ref\": \"#/definitions/v1OutOfStoreMessage\"\n        },\n        \"cleartext\": {\n          \"type\": \"string\",\n          \"format\": \"byte\"\n        },\n        \"group_public_key\": {\n          \"type\": \"string\",\n          \"format\": \"byte\"\n        },\n        \"already_received\": {\n          \"type\": \"boolean\"\n        }\n      }\n    },\n    \"v1OutOfStoreSealReply\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"encrypted\": {\n          \"type\": \"string\",\n          \"format\": \"byte\"\n        }\n      }\n    },\n    \"v1PeerListPeer\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"id\": {\n          \"type\": \"string\",\n          \"description\": \"id is the libp2p.PeerID.\"\n        },\n        \"routes\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"$ref\": \"#/definitions/PeerListRoute\"\n          },\n          \"description\": \"routes are the list of active and known maddr.\"\n        },\n        \"errors\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\"\n          },\n          \"description\": \"errors is a list of errors related to the peer.\"\n        },\n        \"features\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"$ref\": \"#/definitions/PeerListFeature\"\n          },\n          \"description\": \"Features is a list of available features.\"\n        },\n        \"min_latency\": {\n          \"type\": \"string\",\n          \"format\": \"int64\",\n          \"description\": \"MinLatency is the minimum latency across all the peer routes.\"\n        },\n        \"is_active\": {\n          \"type\": \"boolean\",\n          \"description\": \"IsActive is true if at least one of the route is active.\"\n        },\n        \"direction\": {\n          \"$ref\": \"#/definitions/v1Direction\",\n          \"description\": \"Direction is the aggregate of all the routes's direction.\"\n        }\n      }\n    },\n    \"v1PeerListReply\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"peers\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"$ref\": \"#/definitions/v1PeerListPeer\"\n          }\n        }\n      }\n    },\n    \"v1ProtocolMetadata\": {\n      \"type\": \"object\"\n    },\n    \"v1RefreshContactRequestPeer\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"id\": {\n          \"type\": \"string\",\n          \"description\": \"id is the libp2p.PeerID.\"\n        },\n        \"addrs\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\"\n          },\n          \"description\": \"list of peers multiaddrs.\"\n        }\n      }\n    },\n    \"v1RefreshContactRequestReply\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"peers_found\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"$ref\": \"#/definitions/v1RefreshContactRequestPeer\"\n          },\n          \"description\": \"peers found and successfully connected.\"\n        }\n      }\n    },\n    \"v1ReplicationServiceRegisterGroupReply\": {\n      \"type\": \"object\"\n    },\n    \"v1ServiceExportDataReply\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"exported_data\": {\n          \"type\": \"string\",\n          \"format\": \"byte\"\n        }\n      }\n    },\n    \"v1ServiceGetConfigurationReply\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"account_pk\": {\n          \"type\": \"string\",\n          \"format\": \"byte\",\n          \"title\": \"account_pk is the public key of the current account\"\n        },\n        \"device_pk\": {\n          \"type\": \"string\",\n          \"format\": \"byte\",\n          \"title\": \"device_pk is the public key of the current device\"\n        },\n        \"account_group_pk\": {\n          \"type\": \"string\",\n          \"format\": \"byte\",\n          \"title\": \"account_group_pk is the public key of the account group\"\n        },\n        \"peer_id\": {\n          \"type\": \"string\",\n          \"title\": \"peer_id is the peer ID of the current IPFS node\"\n        },\n        \"listeners\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\"\n          },\n          \"title\": \"listeners is the list of swarm listening addresses of the current IPFS node\"\n        },\n        \"ble_enabled\": {\n          \"$ref\": \"#/definitions/ServiceGetConfigurationSettingState\"\n        },\n        \"wifi_p2p_enabled\": {\n          \"$ref\": \"#/definitions/ServiceGetConfigurationSettingState\"\n        },\n        \"mdns_enabled\": {\n          \"$ref\": \"#/definitions/ServiceGetConfigurationSettingState\"\n        },\n        \"relay_enabled\": {\n          \"$ref\": \"#/definitions/ServiceGetConfigurationSettingState\"\n        }\n      }\n    },\n    \"v1ShareContactReply\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"encoded_contact\": {\n          \"type\": \"string\",\n          \"format\": \"byte\",\n          \"description\": \"encoded_contact is the Protobuf encoding of the ShareableContact. You can further encode the bytes for sharing, such as base58 or QR code.\"\n        }\n      }\n    },\n    \"v1ShareableContact\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"pk\": {\n          \"type\": \"string\",\n          \"format\": \"byte\",\n          \"title\": \"pk is the account to send a contact request to\"\n        },\n        \"public_rendezvous_seed\": {\n          \"type\": \"string\",\n          \"format\": \"byte\",\n          \"title\": \"public_rendezvous_seed is the rendezvous seed used by the account to send a contact request to\"\n        },\n        \"metadata\": {\n          \"type\": \"string\",\n          \"format\": \"byte\",\n          \"title\": \"metadata is the metadata specific to the app to identify the contact for the request\"\n        }\n      }\n    },\n    \"v1SystemInfoReply\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"process\": {\n          \"$ref\": \"#/definitions/SystemInfoProcess\"\n        },\n        \"p2p\": {\n          \"$ref\": \"#/definitions/SystemInfoP2P\"\n        },\n        \"orbitdb\": {\n          \"$ref\": \"#/definitions/SystemInfoOrbitDB\"\n        },\n        \"warns\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\"\n          }\n        }\n      }\n    },\n    \"v1VerifiedCredentialsListReply\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"credential\": {\n          \"$ref\": \"#/definitions/v1AccountVerifiedCredentialRegistered\"\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "docs/architecture/2020-11-27-adr-berty-grpc-bridge.txt",
    "content": "┌──────────────────────────────────────────────────────────────────────────────────────────┐\n│service BridgeService {                                                                   │\n│  // CreateClient client a new bridge client                                              │\n│  rpc CreateClient (CreateClient.Request) returns (CreateClient.Reply);                   │\n│                                                                                          │\n│  // ClientInvokeUnary invoke a unary method                                              │\n│  rpc ClientInvokeUnary (ClientInvokeUnary.Request) returns (ClientInvokeUnary.Reply);    │\n│                                                                                          │\n│  // CreateStream create a stream                                                         │\n│  rpc CreateClientStream (ClientCreateStream.Request) returns (ClientCreateStream.Reply); │\n│                                                                                          │\n│  // Send Message over the given stream                                                   │\n│  rpc ClientStreamSend (ClientStreamSend.Request) returns (ClientStreamSend.Reply);       │\n│                                                                                          │\n│  // Recv message over the given stream                                                   │\n│  rpc ClientStreamRecv (ClientStreamRecv.Request) returns (ClientStreamRecv.Reply);       │\n│                                                                                          │\n│  // Close the given stream                                                               │\n│  rpc ClientStreamClose (ClientStreamClose.Request) returns (ClientStreamClose.Reply);    │\n│}                                                                                         │\n└──────────────────────────────────────────────────────────────────────────────────────────┘\n                                              │\n                                              │\n                                              │\n                                              │\n                                              │\n                       ┌──────────────────────┘\n                       │\n                       │\n┏━━━━━━┳───────────────┼────────────────────────────────────────────┐       ┏━━━━━━━━━━━━━━━━━━━━━━━━┳─────────────────────┐           ┏━━━━━━┳──────────────────────────────────────────────────────────────┐\n┃  JS  ┃               │                                            │       ┃  NATIVE (ios/android)  ┃                     │           ┃  Go  ┃                                                              │\n┣━━━━━━┛               ◎                                            │       ┣━━━━━━━━━━━━━━━━━━━━━━━━┛                     │           ┣━━━━━━┛                                                              │\n│            ┌──────────────────┐         ┌──────────────┐          │       │                                              │           │                                                                     │\n│            │                  │         │              │          │       │                                              │           │                                                                     │\n│            │                  │         │              │          │       │                                              │           │                                                                     │\n│            │                  │         │              │          │       │                                              │           │    ┌────────────┐     ┌────────────────┐     ┌──────────────────┐   │\n│            │                  │         │  RPC native  │          │       │ ┌───────────────────────────────────────────┐│           │    │            │     │                │     │                  │   │\n│            │  Bridge Service  │         │  Transport   │          │       │ │                                           ││           │    │   Buffer   │     │   ClientConn   │     │  Bridge Service  │   │\n│         ┌─▶│      Client      │────────▶│ (Unary Only) │──────────┼───────┼▶│InvokeMethod (Base64EncodedMessage: String)│├───────────┼───▶│  Listener  │────▶│(Bridge Service)│────▶│      Server      │──┐│\n│         │  │                  │         │              │          │       │ │                                           ││           │    │            │     │                │     │                  │  ││\n│         │  │                  │         │              │          │       │ └───────────────────────────────────────────┘│           │    └────────────┘     └────────────────┘     └──────────────────┘  ││\n│         │  │                  │         │              │          │       │                                              │           │                                                                    ││\n│         │  │                  │         │              │          │       │                                              │           │                                                                    ││\n│         │  └──────────────────┘         └──────────────┘          │       │                                              │           │       ┌────────────────────────────────────────────────────────────┘│\n│         │                                                         │       │                                              │           │       │                                                             │\n│         └─────────────────────────────────────────────────┐       │       │                                              │           │       │                                                             │\n│                                                           │       │       │                                              │           │       │                                                             │\n│                                         ┌──────────────┐  │       │       │                                              │           │       │  ┌────────────────┐                                         │\n│                                         │              │  │       │       └──────────────────────────────────────────────┘           │       │  │                │                                         │\n│                                         │              │  │       │                                                                  │       │  │                │                                         │\n│                                         │              │  │       │                                                                  │       │  │                │                                         │\n│                                         │              │  │       │                                                                  │       │  │                │                                         │\n│                                         │              │  │       │                                                                  │       │  │                │                                         │\n│        ┌──────────────────────┐         │              │  │       │                                                                  │       │  │                │               ┌──────────────────────┐  │\n│        │  Messenger Service   │         │              │  │       │                                                                  │       │  │                │               │  Messenger Service   │  │\n│        │        Client        │────┐    │              │  │       │                                                                  │       │  │                │       ┌──────▶│        Server        │  │\n│        │                      │    │    │              │  │       │                                                                  │       │  │                │       │       │                      │  │\n│        └──────────────────────┘    │    │              │  │       │                                                                  │       │  │                │       │       └──────────────────────┘  │\n│        ┌──────────────────────┐    │    │    Bridge    │  │       │                                                                  │       │  │                │       │       ┌──────────────────────┐  │\n│        │   Protocol Service   │    │    │  Transport   │  │       │                                                                  │       │  │GRPC ClientConn │       │       │   Protocol Service   │  │\n│        │        Client        │────┼───▶│(Stream/Unary)│──┘       │                                                                  │       └─▶│(Mixed Services)│───────┼──────▶│        Server        │  │\n│        │                      │    │    │              │          │                                                                  │          │                │       │       │                      │  │\n│        └──────────────────────┘    │    │              │          │                                                                  │          │                │       │       └──────────────────────┘  │\n│        ┌──────────────────────┐    │    │              │          │                                                                  │          │                │       │       ┌──────────────────────┐  │\n│        │                      │    │    │              │          │                                                                  │          │                │       │       │                      │  │\n│        │         ...          │────┘    │              │          │                                                                  │          │                │       └──────▶│         ...          │  │\n│        │                      │         │              │          │                                                                  │          │                │               │                      │  │\n│        └──────────────────────┘         │              │          │                                                                  │          │                │               └──────────────────────┘  │\n│                                         │              │          │                                                                  │          │                │                                         │\n│                                         │              │          │                                                                  │          │                │                                         │\n│                                         │              │          │                                                                  │          │                │                                         │\n│                                         │              │          │                                                                  │          │                │                                         │\n│                                         └──────────────┘          │                                                                  │          └────────────────┘                                         │\n│                                                                   │                                                                  │                                                                     │\n│                                                                   │                                                                  │                                                                     │\n│                                                                   │                                                                  │                                                                     │\n└───────────────────────────────────────────────────────────────────┘                                                                  └─────────────────────────────────────────────────────────────────────┘\n"
  },
  {
    "path": "docs/architecture/2020-11-27-adr-gomobile-ipfs.md",
    "content": "# GomobileIPFS\n\n## concept\n\n### Driver\n```\n┌───────────────┐             ┌───────────────┐            ┌───────────────┐\n│               │             │               │            │               │\n│    Native     │             │  Init Driver  │            │      Go       │\n│ (IOS/Android) │────────────▶│               │───────────▶│               │\n│               │             │               │            │               │\n└───────────────┘             └───────────────┘            └───────────────┘\n        │                                                          │\n        │                                                          │\n        │                                                          │\n        │                                                          ▼\n        │                                                  ┌───────────────┐\n        │                                                  │               │\n        │                         implement                │   Interface   │\n        └─────────────────────────────────────────────────▶│   (Driver)    │\n                                                           │               │\n                                                           └───────────────┘\n```\n\n- Go can call Native by calling **Driver** method directly\n- Native can call Go Method with **Driver** `RegisterHandler` method\n\n#### RegisterHandler example\n_example from berty_\n```go\n// BackgroundTask\n\ntype BackgroundTaskHandlerDriver interface {\n    RegisterHandler(BackgroundTaskHandler)\n}\n\ntype BackgroundTaskHandler interface {\n    HandleTask() LifeCycleBackgroundTask\n}\n\ntype BackgroundTaskDriver interface {\n    Execute() (success bool)\n}\n```\n[berty](https://github.com/berty/berty/blob/master/go/framework/bertynative/driver_lifecycle.go)\n\n\n##### implement in swift\n```swift\n// BackgroundTaskDriver implement BackgroundTaskHandler\npublic class BackgroundTaskDriver: BackgroundTaskHandlerDriverProtocol {\n    let handler: BackgroundTaskHandlerProtocol\n    func registerHandler(handler: BackgroundTaskHandlerProtocol) {\n            self.handler = handler\n    }\n\n    func executeGoBackgroundTask() {\n          let success = self.handler.Execute()\n    }\n}\n```\n[berty](https://github.com/berty/berty/blob/master/js/ios/Berty/Sources/LifeCycle.swift#L24)\n\n##### Usage in gomobileipfs\nuser will then simply register the driver to ios background task system then pass it to go\n```swift\n// pseudo code below\n// ...\nbackgroundDriver = BackgroundTaskDriver()\n\n// user register\nregisterLifecycleBackgroundTaskToIOS(\"myapp.ios.background-task\", backgroundDriver)\n\n// go node init\nlet node = IPFSNode()\nnode.withBackgroundTaskDriver(backgroundDriver)\n// ...\n```\n[berty](https://github.com/berty/berty/blob/master/js/ios/Berty/AppDelegate.m#L54)\n\n\n### GomobileIPFS \\w berty\n\n\n```\n╔════════╦━━━━━━━━━━━━━━━━┓        ┏╦═════════════╦━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n║ Berty  ║                ┃        ┃║GomobileIPFS ║                                                     ┃\n╠════════╝                ┃        ┃╚═════════════╝                                                     ┃\n┃                         ┃        ┃                                                                    ┃\n┃     ┌────────────┐      ┃        ┃                           ┌────────────┐                           ┃\n┃     │            │      ┃        ┃                           │            │                           ┃\n┃     │    pkg     │      ┃        ┃                           │    pkg     │                           ┃\n┃     │            │      ┃        ┃                           │            │                           ┃\n┃     └────────────┘      ┃        ┃                           └────────────┘                           ┃\n┃            │            ┃        ┃                                  │                                 ┃\n┃            │            ┃        ┃               ┌──────────────────┴────────────────┐                ┃\n┃            ▼            ┃        ┃               ▼                                   ▼                ┃\n┃     ┌────────────┐      ┃        ┃        ┌────────────┐                      ┌────────────┐          ┃\n┃     │            │      ┃        ┃        │            │                      │            │          ┃\n┃     │   bridge   │◀─────╋─import─╋────────│   driver   │────────import─┐      │    node    │          ┃\n┃     │            │      ┃        ┃        │            │               │      │            │          ┃\n┃     └────────────┘      ┃        ┃        └────────────┘               │      └────────────┘          ┃\n┃            │            ┃        ┃            ▲     ▲                  │             │                ┃\n┃            │            ┃        ┃            │     │                  │             └───────┐        ┃\n┃            │            ┃        ┃            │     │                  │                     ▼        ┃\n┃            │            ┃        ┃            │     │                  │               ┌───────────┐  ┃\n┃            │            ┃        ┃            │     │                  │               │           │  ┃\n┃            │            ┃        ┃            │     │                  └──────────────▶│   bind    │  ┃\n┃            │            ┃        ┃            │     │                                  │           │  ┃\n┃            │            ┃        ┃            │     │                                  └───────────┘  ┃\n┃            │            ┃        ┃            │     │                                        │        ┃\n┃            │            ┃        ┃            │     │                                        │        ┃\n┃            │            ┃        ┃           implement                                       │        ┃\n┃            │            ┃        ┃            │     │                                        │        ┃\n┃            │            ┃        ┃            │     │                                        │        ┃\n┃          gobind         ┃        ┃            │     │                                      gobind     ┃\n┃            │            ┃        ┃            │     │                                        │        ┃\n┃            │            ┃        ┃            │     │                                        │        ┃\n┃            │            ┃        ┃            │     │                                        │        ┃\n┃            │            ┃        ┃            │     │                                        │        ┃\n┃            │            ┃        ┃            │     │                                        │        ┃\n┃            ▼            ┃        ┃            │     │                                        ▼        ┃\n┃     ┏━━━━━━━━━━━━┓      ┃        ┃            │     │                                 ┏━━━━━━━━━━━━┓  ┃\n┃     ┃   Berty    ┃      ┃        ┃            │     │                                 ┃GomobileIPFS┃  ┃\n┃     ┃ Framework  ┃──────╋────────╋────────────┘     └─────────────────────────────────┃ Framework  ┃  ┃\n┃     ┃            ┃      ┃        ┃                                                    ┃            ┃  ┃\n┃     ┗━━━━━━━━━━━━┛      ┃        ┃                                                    ┗━━━━━━━━━━━━┛  ┃\n┗━━━━━━━━━━━━━━━━━━━━━━━━━┛        ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n```\n"
  },
  {
    "path": "docs/architecture/README.md",
    "content": "# Architecture\n\nThis folder mostly contains snapshots of the architecture at Berty, some documentations may be outdated, but reflect our feeling at a particular period of the project.\n"
  },
  {
    "path": "docs/architecture/messenger-mvp/README.md",
    "content": "# Berty Messenger MVP\n\n## Reading linked graphs\n\n### Account group/log back and forth ignored\n\nSending an event from the protocol to the app actually requires the app to subscribe to the account log, the protocol then adds the events it wants to send to the app on the account log. It was simplified by drawing an edge from the protocol to the app directly\n\n## Contact request\n\n![Graph](contact-request.mermaid.svg)\n"
  },
  {
    "path": "docs/architecture/messenger-mvp/contact-request.mermaid",
    "content": "sequenceDiagram\n\nparticipant aorbitdb as Alice orbitdb\nparticipant aprotocol as Alice fake protocol\nparticipant achat as Alice chat app\nparticipant a as Alice\nparticipant b as Bob\nparticipant bchat as Bob chat app\nparticipant bprotocol as Bob fake protocol\nparticipant borbitdb as Bob orbitdb\n\na->>achat: Start app\nachat->>aprotocol: Call contactRequestEnable command\naprotocol-->>aorbitdb: Subscribe to Alice rdv log\naprotocol->>achat: Send contactRequestEnabled event with reference\nachat->>a: Display QR code with reference and Alice metadata\na->>b: Show/Send QR with Alice reference and contact metadata\nb->>bchat: Scan QR with Alice reference and contact metadata\nbchat-->> bprotocol: Subscribe to 1to1 group metadata\nbprotocol-->> borbitdb: Subscribe to 1to1 group metadata log\nbchat->>bprotocol: Call sendContactRequest command\nbprotocol-->> bchat: Send contactRequestEnqueued event\nbchat-->> b: Display an outgoing contact request\nbprotocol->>borbitdb: Send Bob fake public key, 1to1 group fake public key and contact metadata on Alice rdv log\nborbitdb->>aorbitdb: Alice rdv log replication\naorbitdb->>aprotocol: Send contact request with bob data from rdv log\naprotocol->>achat: Send contactRequestReceived event\nachat->> a: Display contact request\na->> achat: Touch accept contact request\nachat->> aprotocol: Call contactRequestAccept command\naprotocol->> aorbitdb: Send groupMemberDeviceAdded event on the 1to1 group metadata log\naorbitdb->>borbitdb: 1to1 group metadata log replication\nborbitdb->> bprotocol: Send groupMemberDeviceAdded event from the 1to1 group metadata log\nbprotocol->> bchat: Send groupMemberDeviceAdded event from the 1to1 group metadata\nbchat->> b: Show that the request is accepted\n"
  },
  {
    "path": "docs/buf-doc.gen.yaml",
    "content": "version: v1\nplugins:\n  - plugin: doc\n    out: ./\n    opt: markdown,api.md.tmp\n  - plugin: swagger\n    out: ./\n    opt:\n      - logtostderr=true\n"
  },
  {
    "path": "docs/gen.sum",
    "content": "10db4498e1b002bb5bbfeb67ec3ce4d259b35c3d  Makefile\n"
  },
  {
    "path": "docs/ideas/distributed-entropy.md",
    "content": "# Distributed entropy\n\nDate: 2019-05-24  \nAuthor: Antoine Eddi (aeddi)\n\n## Description\n\nThe idea would be to exchange random data blocks between trusted devices (owned by the same account or by different contacts).\nThe exchange will take place during device-to-device handshakes, we could add a step to our handshake protocol to exchange 64 bytes of random data.\nAll the random data collected from different sources could be mixed up and stored securely so that it could be reused later to generate new cryptographic materials.\n\nNB: It is obviously out of the question to use the data provided by a single peer without mixing it with data from other sources to generate secrets.\n\n\n## Why?\n\nA particular device could have an unsafe PRNG for some reason. Mixing random data provided by different devices should prevent an attack based on PNRG weaknesses specific to a particular device.\n\n\n## To be considered\n\nWe don't know enough about the subject to assess whether it's a bad practice to do this kind of thing.\n"
  },
  {
    "path": "docs/ideas/entropy-pool.md",
    "content": "# Entropy pool\n\nDate: 2019-05-24  \nAuthor: Manfred Touron (moul)\n\n## Description\n\nThe idea would be to add to a pool random data blocks generated when the level of entropy provided by the OS or by dedicated hardware is at its highest.\nRandom data added to the pool could be reused later to generate new cryptographic materials.\n\nWe could ensure that the pool is built when the device is idle and its battery is fully charged.\nFor example, in the case of a phone: when it charges during the night.\n\n\n## Why?\n\nAn attacker could lower the entropy level at some point and make the numbers generated predictable.\n\n\n## To be considered\n\nWe don't know enough about the subject to assess whether it's a bad practice to do this kind of thing.\n"
  },
  {
    "path": "docs/protocol/README.md",
    "content": "# Wesh protocol\n\nThe Wesh protocol is documented at https://berty.tech/docs/protocol .\n"
  },
  {
    "path": "events.go",
    "content": "package weshnet\n\nimport (\n\t\"fmt\"\n\n\tcid \"github.com/ipfs/go-cid\"\n\t\"golang.org/x/crypto/nacl/secretbox\"\n\t\"google.golang.org/protobuf/proto\"\n\n\tipfslog \"berty.tech/go-ipfs-log\"\n\t\"berty.tech/weshnet/v2/pkg/cryptoutil\"\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n)\n\nvar eventTypesMapper = map[protocoltypes.EventType]struct {\n\tMessage    proto.Message\n\tSigChecker sigChecker\n}{\n\tprotocoltypes.EventType_EventTypeGroupMemberDeviceAdded:                 {Message: &protocoltypes.GroupMemberDeviceAdded{}, SigChecker: sigCheckerGroupMemberDeviceAdded},\n\tprotocoltypes.EventType_EventTypeGroupDeviceChainKeyAdded:               {Message: &protocoltypes.GroupDeviceChainKeyAdded{}, SigChecker: sigCheckerDeviceSigned},\n\tprotocoltypes.EventType_EventTypeAccountGroupJoined:                     {Message: &protocoltypes.AccountGroupJoined{}, SigChecker: sigCheckerDeviceSigned},\n\tprotocoltypes.EventType_EventTypeAccountGroupLeft:                       {Message: &protocoltypes.AccountGroupLeft{}, SigChecker: sigCheckerDeviceSigned},\n\tprotocoltypes.EventType_EventTypeAccountContactRequestDisabled:          {Message: &protocoltypes.AccountContactRequestDisabled{}, SigChecker: sigCheckerDeviceSigned},\n\tprotocoltypes.EventType_EventTypeAccountContactRequestEnabled:           {Message: &protocoltypes.AccountContactRequestEnabled{}, SigChecker: sigCheckerDeviceSigned},\n\tprotocoltypes.EventType_EventTypeAccountContactRequestReferenceReset:    {Message: &protocoltypes.AccountContactRequestReferenceReset{}, SigChecker: sigCheckerDeviceSigned},\n\tprotocoltypes.EventType_EventTypeAccountContactRequestOutgoingEnqueued:  {Message: &protocoltypes.AccountContactRequestOutgoingEnqueued{}, SigChecker: sigCheckerDeviceSigned},\n\tprotocoltypes.EventType_EventTypeAccountContactRequestOutgoingSent:      {Message: &protocoltypes.AccountContactRequestOutgoingSent{}, SigChecker: sigCheckerDeviceSigned},\n\tprotocoltypes.EventType_EventTypeAccountContactRequestIncomingReceived:  {Message: &protocoltypes.AccountContactRequestIncomingReceived{}, SigChecker: sigCheckerDeviceSigned},\n\tprotocoltypes.EventType_EventTypeAccountContactRequestIncomingDiscarded: {Message: &protocoltypes.AccountContactRequestIncomingDiscarded{}, SigChecker: sigCheckerDeviceSigned},\n\tprotocoltypes.EventType_EventTypeAccountContactRequestIncomingAccepted:  {Message: &protocoltypes.AccountContactRequestIncomingAccepted{}, SigChecker: sigCheckerDeviceSigned},\n\tprotocoltypes.EventType_EventTypeAccountContactBlocked:                  {Message: &protocoltypes.AccountContactBlocked{}, SigChecker: sigCheckerDeviceSigned},\n\tprotocoltypes.EventType_EventTypeAccountContactUnblocked:                {Message: &protocoltypes.AccountContactUnblocked{}, SigChecker: sigCheckerDeviceSigned},\n\tprotocoltypes.EventType_EventTypeContactAliasKeyAdded:                   {Message: &protocoltypes.ContactAliasKeyAdded{}, SigChecker: sigCheckerDeviceSigned},\n\tprotocoltypes.EventType_EventTypeMultiMemberGroupAliasResolverAdded:     {Message: &protocoltypes.MultiMemberGroupAliasResolverAdded{}, SigChecker: sigCheckerDeviceSigned},\n\tprotocoltypes.EventType_EventTypeMultiMemberGroupInitialMemberAnnounced: {Message: &protocoltypes.MultiMemberGroupInitialMemberAnnounced{}, SigChecker: sigCheckerGroupSigned},\n\tprotocoltypes.EventType_EventTypeMultiMemberGroupAdminRoleGranted:       {Message: &protocoltypes.MultiMemberGroupAdminRoleGranted{}, SigChecker: sigCheckerDeviceSigned},\n\tprotocoltypes.EventType_EventTypeGroupMetadataPayloadSent:               {Message: &protocoltypes.GroupMetadataPayloadSent{}, SigChecker: sigCheckerDeviceSigned},\n\tprotocoltypes.EventType_EventTypeGroupReplicating:                       {Message: &protocoltypes.GroupReplicating{}, SigChecker: sigCheckerDeviceSigned},\n\tprotocoltypes.EventType_EventTypeAccountVerifiedCredentialRegistered:    {Message: &protocoltypes.AccountVerifiedCredentialRegistered{}, SigChecker: sigCheckerDeviceSigned},\n}\n\nfunc newEventContext(eventID cid.Cid, parentIDs []cid.Cid, g *protocoltypes.Group) *protocoltypes.EventContext {\n\tparentIDsBytes := make([][]byte, len(parentIDs))\n\tfor i, parentID := range parentIDs {\n\t\tparentIDsBytes[i] = parentID.Bytes()\n\t}\n\n\treturn &protocoltypes.EventContext{\n\t\tId:        eventID.Bytes(),\n\t\tParentIds: parentIDsBytes,\n\t\tGroupPk:   g.PublicKey,\n\t}\n}\n\n// FIXME(gfanton): getParentsCID use a lot of resources\n// nolint:unused\nfunc getParentsForCID(log ipfslog.Log, c cid.Cid) []cid.Cid {\n\tif log == nil {\n\t\t// TODO: this should not happen\n\t\treturn []cid.Cid{}\n\t}\n\n\tparent, ok := log.Get(c)\n\n\t// Can't fetch parent entry\n\tif !ok {\n\t\treturn []cid.Cid{}\n\t}\n\n\tnextEntries := parent.GetNext()\n\n\t// Parent has only one or no parents, returning its id\n\tif len(nextEntries) <= 1 {\n\t\treturn []cid.Cid{parent.GetHash()}\n\t}\n\n\t// Parent has more than one parent, returning parent entries\n\tvar ret []cid.Cid\n\tfor _, n := range nextEntries {\n\t\tret = append(ret, getParentsForCID(log, n)...)\n\t}\n\n\treturn ret\n}\n\nfunc newGroupMetadataEventFromEntry(_ ipfslog.Log, e ipfslog.Entry, metadata *protocoltypes.GroupMetadata, event proto.Message, g *protocoltypes.Group) (*protocoltypes.GroupMetadataEvent, error) {\n\t// TODO: if parent is a merge node we should return the next nodes of it\n\n\teventBytes, err := proto.Marshal(event)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrSerialization\n\t}\n\n\t// TODO(gfanton): getParentsCID use a lot of resources, disable it until we need it\n\t// evtCtx := newEventContext(e.GetHash(), getParentsForCID(log, e.GetHash()), group, attachmentsCIDs)\n\tevtCtx := newEventContext(e.GetHash(), []cid.Cid{}, g)\n\n\tgme := protocoltypes.GroupMetadataEvent{\n\t\tEventContext: evtCtx,\n\t\tMetadata:     metadata,\n\t\tEvent:        eventBytes,\n\t}\n\n\treturn &gme, nil\n}\n\nfunc openGroupEnvelope(g *protocoltypes.Group, envelopeBytes []byte) (*protocoltypes.GroupMetadata, proto.Message, error) {\n\tenv := &protocoltypes.GroupEnvelope{}\n\tif err := proto.Unmarshal(envelopeBytes, env); err != nil {\n\t\treturn nil, nil, errcode.ErrCode_ErrInvalidInput.Wrap(err)\n\t}\n\n\tnonce, err := cryptoutil.NonceSliceToArray(env.Nonce)\n\tif err != nil {\n\t\treturn nil, nil, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\tdata, ok := secretbox.Open(nil, env.Event, nonce, g.GetSharedSecret())\n\tif !ok {\n\t\treturn nil, nil, errcode.ErrCode_ErrGroupMemberLogEventOpen\n\t}\n\n\tmetadataEvent := &protocoltypes.GroupMetadata{}\n\n\terr = proto.Unmarshal(data, metadataEvent)\n\tif err != nil {\n\t\treturn nil, nil, errcode.ErrCode_TODO.Wrap(err)\n\t}\n\n\tet, ok := eventTypesMapper[metadataEvent.EventType]\n\tif !ok {\n\t\treturn nil, nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"event type not found\"))\n\t}\n\n\tpayload := proto.Clone(et.Message)\n\tif err := proto.Unmarshal(metadataEvent.Payload, payload); err != nil {\n\t\treturn nil, nil, errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\tif err := et.SigChecker(g, metadataEvent, payload); err != nil {\n\t\treturn nil, nil, errcode.ErrCode_ErrCryptoSignatureVerification.Wrap(err)\n\t}\n\n\treturn metadataEvent, payload, nil\n}\n\nfunc sealGroupEnvelope(g *protocoltypes.Group, eventType protocoltypes.EventType, payload proto.Message, payloadSig []byte) ([]byte, error) {\n\tpayloadBytes, err := proto.Marshal(payload)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_TODO.Wrap(err)\n\t}\n\n\tnonce, err := cryptoutil.GenerateNonce()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrCryptoNonceGeneration.Wrap(err)\n\t}\n\n\tevent := &protocoltypes.GroupMetadata{\n\t\tEventType:        eventType,\n\t\tPayload:          payloadBytes,\n\t\tSig:              payloadSig,\n\t\tProtocolMetadata: &protocoltypes.ProtocolMetadata{},\n\t}\n\n\teventClearBytes, err := proto.Marshal(event)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\teventBytes := secretbox.Seal(nil, eventClearBytes, nonce, g.GetSharedSecret())\n\n\tenv := &protocoltypes.GroupEnvelope{\n\t\tEvent: eventBytes,\n\t\tNonce: nonce[:],\n\t}\n\n\treturn proto.Marshal(env)\n}\n"
  },
  {
    "path": "events_sig_checkers.go",
    "content": "package weshnet\n\nimport (\n\t\"github.com/libp2p/go-libp2p/core/crypto\"\n\t\"google.golang.org/protobuf/proto\"\n\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n)\n\ntype sigChecker func(g *protocoltypes.Group, metadata *protocoltypes.GroupMetadata, message proto.Message) error\n\nfunc sigCheckerGroupSigned(g *protocoltypes.Group, metadata *protocoltypes.GroupMetadata, _ proto.Message) error {\n\tpk, err := g.GetPubKey()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tok, err := pk.Verify(metadata.Payload, metadata.Sig)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrCryptoSignatureVerification.Wrap(err)\n\t}\n\n\tif !ok {\n\t\treturn errcode.ErrCode_ErrCryptoSignatureVerification\n\t}\n\n\treturn nil\n}\n\ntype eventDeviceSigned interface {\n\tproto.Message\n\tGetDevicePk() []byte\n}\n\nfunc sigCheckerDeviceSigned(_ *protocoltypes.Group, metadata *protocoltypes.GroupMetadata, message proto.Message) error {\n\tmsg, ok := message.(eventDeviceSigned)\n\tif !ok {\n\t\treturn errcode.ErrCode_ErrDeserialization\n\t}\n\n\tdevPK, err := crypto.UnmarshalEd25519PublicKey(msg.GetDevicePk())\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\tok, err = devPK.Verify(metadata.Payload, metadata.Sig)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrCryptoSignatureVerification.Wrap(err)\n\t}\n\n\tif !ok {\n\t\treturn errcode.ErrCode_ErrCryptoSignatureVerification\n\t}\n\n\treturn nil\n}\n\nfunc sigCheckerGroupMemberDeviceAdded(g *protocoltypes.Group, metadata *protocoltypes.GroupMetadata, message proto.Message) error {\n\tmsg, ok := message.(*protocoltypes.GroupMemberDeviceAdded)\n\tif !ok {\n\t\treturn errcode.ErrCode_ErrDeserialization\n\t}\n\n\tmemPK, err := crypto.UnmarshalEd25519PublicKey(msg.MemberPk)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\tok, err = memPK.Verify(msg.DevicePk, msg.MemberSig)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrCryptoSignatureVerification.Wrap(err)\n\t}\n\n\tif !ok {\n\t\treturn errcode.ErrCode_ErrCryptoSignatureVerification\n\t}\n\n\treturn sigCheckerDeviceSigned(g, metadata, message)\n}\n"
  },
  {
    "path": "gen.sum",
    "content": "78cfa180bfe8caf1572068cd6ea6bee4b89b3348  Makefile\n"
  },
  {
    "path": "go.mod",
    "content": "module berty.tech/weshnet/v2\n\ngo 1.22\n\ntoolchain go1.22.5\n\nrequire (\n\tberty.tech/go-ipfs-log v1.10.3-0.20240719141234-29e2d26e2aeb\n\tberty.tech/go-ipfs-repo-encrypted v1.3.1-0.20240722095251-c6b363b38785\n\tberty.tech/go-orbit-db v1.22.2-0.20240719144258-ec7d1faaca68\n\tfilippo.io/edwards25519 v1.0.0\n\tgithub.com/aead/ecdh v0.2.0\n\tgithub.com/berty/emitter-go v0.0.0-20221031144724-5dae963c3622\n\tgithub.com/berty/go-libp2p-rendezvous v0.5.1\n\tgithub.com/buicongtan1997/protoc-gen-swagger-config v0.0.0-20200705084907-1342b78c1a7e\n\tgithub.com/daixiang0/gci v0.8.2\n\tgithub.com/dgraph-io/badger/v2 v2.2007.3\n\tgithub.com/gofrs/uuid v4.3.1+incompatible\n\tgithub.com/golang/protobuf v1.5.4\n\tgithub.com/grpc-ecosystem/go-grpc-middleware v1.4.0\n\tgithub.com/grpc-ecosystem/grpc-gateway v1.16.0\n\tgithub.com/hyperledger/aries-framework-go v0.1.9-0.20221202141134-083803ecf0a3\n\tgithub.com/ipfs/go-cid v0.4.1\n\tgithub.com/ipfs/go-datastore v0.6.0\n\tgithub.com/ipfs/go-ds-badger2 v0.1.3\n\tgithub.com/ipfs/go-ipfs-keystore v0.1.0\n\tgithub.com/ipfs/go-ipld-cbor v0.1.0\n\tgithub.com/ipfs/go-log/v2 v2.5.1\n\tgithub.com/ipfs/kubo v0.29.0\n\tgithub.com/juju/fslock v0.0.0-20160525022230-4d5c94c67b4b\n\tgithub.com/libp2p/go-libp2p v0.34.1\n\tgithub.com/libp2p/go-libp2p-kad-dht v0.25.2\n\tgithub.com/libp2p/go-libp2p-pubsub v0.11.1-0.20240711152552-e508d8643ddb\n\tgithub.com/libp2p/go-libp2p-testing v0.12.0\n\tgithub.com/mdomke/git-semver/v5 v5.0.0\n\tgithub.com/multiformats/go-multiaddr v0.12.4\n\tgithub.com/multiformats/go-multiaddr-dns v0.3.1\n\tgithub.com/multiformats/go-multiaddr-fmt v0.1.0\n\tgithub.com/multiformats/go-multibase v0.2.0\n\tgithub.com/multiformats/go-multihash v0.2.3\n\tgithub.com/piprate/json-gold v0.4.2\n\tgithub.com/pkg/errors v0.9.1\n\tgithub.com/prometheus/client_golang v1.19.1\n\tgithub.com/pseudomuto/protoc-gen-doc v1.5.1\n\tgithub.com/srikrsna/protoc-gen-gotag v1.0.1\n\tgithub.com/stretchr/testify v1.9.0\n\tgo.uber.org/goleak v1.3.0\n\tgo.uber.org/multierr v1.11.0\n\tgo.uber.org/zap v1.27.0\n\tgolang.org/x/crypto v0.24.0\n\tgolang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d\n\tgolang.org/x/xerrors v0.0.0-20231012003039-104605ab7028\n\tgoogle.golang.org/grpc v1.65.0\n\tgoogle.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1\n\tgoogle.golang.org/grpc/examples v0.0.0-20200922230038-4e932bbcb079\n\tgoogle.golang.org/protobuf v1.34.2\n\tmoul.io/openfiles v1.2.0\n\tmoul.io/srand v1.6.1\n\tmoul.io/testman v1.5.0\n\tmoul.io/u v1.27.0\n\tmoul.io/zapfilter v1.7.0\n\tmoul.io/zapring v1.3.3\n\tmvdan.cc/gofumpt v0.4.0\n)\n\nrequire (\n\tbazil.org/fuse v0.0.0-20200117225306-7b5117fecadc // indirect\n\tcontrib.go.opencensus.io/exporter/prometheus v0.4.2 // indirect\n\tgithub.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect\n\tgithub.com/DataDog/zstd v1.4.1 // indirect\n\tgithub.com/Jorropo/jsync v1.0.1 // indirect\n\tgithub.com/Masterminds/goutils v1.1.0 // indirect\n\tgithub.com/Masterminds/semver v1.5.0 // indirect\n\tgithub.com/Masterminds/sprig v2.22.0+incompatible // indirect\n\tgithub.com/VictoriaMetrics/fastcache v1.5.7 // indirect\n\tgithub.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9 // indirect\n\tgithub.com/alexbrainman/goissue34681 v0.0.0-20191006012335-3fc7a47baff5 // indirect\n\tgithub.com/benbjohnson/clock v1.3.5 // indirect\n\tgithub.com/beorn7/perks v1.0.1 // indirect\n\tgithub.com/blang/semver/v4 v4.0.0 // indirect\n\tgithub.com/btcsuite/btcd v0.22.1 // indirect\n\tgithub.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce // indirect\n\tgithub.com/cenkalti/backoff/v4 v4.3.0 // indirect\n\tgithub.com/ceramicnetwork/go-dag-jose v0.1.0 // indirect\n\tgithub.com/cespare/xxhash v1.1.0 // indirect\n\tgithub.com/cespare/xxhash/v2 v2.3.0 // indirect\n\tgithub.com/cheggaaa/pb v1.0.29 // indirect\n\tgithub.com/containerd/cgroups v1.1.0 // indirect\n\tgithub.com/coreos/go-systemd/v22 v22.5.0 // indirect\n\tgithub.com/crackcomm/go-gitignore v0.0.0-20231225121904-e25f5bc08668 // indirect\n\tgithub.com/cskr/pubsub v1.0.2 // indirect\n\tgithub.com/davecgh/go-spew v1.1.1 // indirect\n\tgithub.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect\n\tgithub.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect\n\tgithub.com/dgraph-io/badger v1.6.2 // indirect\n\tgithub.com/dgraph-io/ristretto v0.0.3 // indirect\n\tgithub.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 // indirect\n\tgithub.com/docker/go-units v0.5.0 // indirect\n\tgithub.com/dustin/go-humanize v1.0.1 // indirect\n\tgithub.com/eclipse/paho.mqtt.golang v1.4.2 // indirect\n\tgithub.com/elastic/gosigar v0.14.2 // indirect\n\tgithub.com/elgris/jsondiff v0.0.0-20160530203242-765b5c24c302 // indirect\n\tgithub.com/emicklei/proto v1.6.13 // indirect\n\tgithub.com/envoyproxy/protoc-gen-validate v1.0.4 // indirect\n\tgithub.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5 // indirect\n\tgithub.com/fatih/color v1.13.0 // indirect\n\tgithub.com/fatih/structtag v1.2.0 // indirect\n\tgithub.com/felixge/httpsnoop v1.0.4 // indirect\n\tgithub.com/flynn/noise v1.1.0 // indirect\n\tgithub.com/francoispqt/gojay v1.2.13 // indirect\n\tgithub.com/fsnotify/fsnotify v1.6.0 // indirect\n\tgithub.com/gabriel-vasile/mimetype v1.4.3 // indirect\n\tgithub.com/ghodss/yaml v1.0.0 // indirect\n\tgithub.com/go-kit/log v0.2.1 // indirect\n\tgithub.com/go-logfmt/logfmt v0.5.1 // indirect\n\tgithub.com/go-logr/logr v1.4.1 // indirect\n\tgithub.com/go-logr/stdr v1.2.2 // indirect\n\tgithub.com/go-task/slim-sprig/v3 v3.0.0 // indirect\n\tgithub.com/godbus/dbus/v5 v5.1.0 // indirect\n\tgithub.com/gogo/protobuf v1.3.2 // indirect\n\tgithub.com/golang/glog v1.2.1 // indirect\n\tgithub.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect\n\tgithub.com/golang/snappy v0.0.4 // indirect\n\tgithub.com/google/go-cmp v0.6.0 // indirect\n\tgithub.com/google/gopacket v1.1.19 // indirect\n\tgithub.com/google/pprof v0.0.0-20240509144519-723abb6459b7 // indirect\n\tgithub.com/google/tink/go v1.7.0 // indirect\n\tgithub.com/google/uuid v1.6.0 // indirect\n\tgithub.com/gorilla/mux v1.8.1 // indirect\n\tgithub.com/gorilla/websocket v1.5.1 // indirect\n\tgithub.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect\n\tgithub.com/hashicorp/errwrap v1.1.0 // indirect\n\tgithub.com/hashicorp/go-multierror v1.1.1 // indirect\n\tgithub.com/hashicorp/golang-lru v1.0.2 // indirect\n\tgithub.com/hashicorp/golang-lru/v2 v2.0.7 // indirect\n\tgithub.com/hexops/gotextdiff v1.0.3 // indirect\n\tgithub.com/huandu/xstrings v1.3.2 // indirect\n\tgithub.com/huin/goupnp v1.3.0 // indirect\n\tgithub.com/hyperledger/aries-framework-go/spi v0.0.0-20221025204933-b807371b6f1e // indirect\n\tgithub.com/hyperledger/ursa-wrapper-go v0.3.1 // indirect\n\tgithub.com/imdario/mergo v0.3.11 // indirect\n\tgithub.com/inconshreveable/mousetrap v1.1.0 // indirect\n\tgithub.com/ipfs-shipyard/nopfs v0.0.12 // indirect\n\tgithub.com/ipfs-shipyard/nopfs/ipfs v0.13.2-0.20231027223058-cde3b5ba964c // indirect\n\tgithub.com/ipfs/bbloom v0.0.4 // indirect\n\tgithub.com/ipfs/boxo v0.20.0 // indirect\n\tgithub.com/ipfs/go-bitfield v1.1.0 // indirect\n\tgithub.com/ipfs/go-block-format v0.2.0 // indirect\n\tgithub.com/ipfs/go-blockservice v0.5.2 // indirect\n\tgithub.com/ipfs/go-cidutil v0.1.0 // indirect\n\tgithub.com/ipfs/go-ds-badger v0.3.0 // indirect\n\tgithub.com/ipfs/go-ds-flatfs v0.5.1 // indirect\n\tgithub.com/ipfs/go-ds-leveldb v0.5.0 // indirect\n\tgithub.com/ipfs/go-ds-measure v0.2.0 // indirect\n\tgithub.com/ipfs/go-ds-sql v0.3.0 // indirect\n\tgithub.com/ipfs/go-fs-lock v0.0.7 // indirect\n\tgithub.com/ipfs/go-ipfs-blockstore v1.3.1 // indirect\n\tgithub.com/ipfs/go-ipfs-cmds v0.11.0 // indirect\n\tgithub.com/ipfs/go-ipfs-delay v0.0.1 // indirect\n\tgithub.com/ipfs/go-ipfs-ds-help v1.1.1 // indirect\n\tgithub.com/ipfs/go-ipfs-exchange-interface v0.2.1 // indirect\n\tgithub.com/ipfs/go-ipfs-pq v0.0.3 // indirect\n\tgithub.com/ipfs/go-ipfs-redirects-file v0.1.1 // indirect\n\tgithub.com/ipfs/go-ipfs-util v0.0.3 // indirect\n\tgithub.com/ipfs/go-ipld-format v0.6.0 // indirect\n\tgithub.com/ipfs/go-ipld-git v0.1.1 // indirect\n\tgithub.com/ipfs/go-ipld-legacy v0.2.1 // indirect\n\tgithub.com/ipfs/go-libipfs v0.6.2 // indirect\n\tgithub.com/ipfs/go-log v1.0.5 // indirect\n\tgithub.com/ipfs/go-merkledag v0.11.0 // indirect\n\tgithub.com/ipfs/go-metrics-interface v0.0.1 // indirect\n\tgithub.com/ipfs/go-peertaskqueue v0.8.1 // indirect\n\tgithub.com/ipfs/go-unixfsnode v1.9.0 // indirect\n\tgithub.com/ipfs/go-verifcid v0.0.3 // indirect\n\tgithub.com/ipld/go-car v0.6.2 // indirect\n\tgithub.com/ipld/go-car/v2 v2.13.1 // indirect\n\tgithub.com/ipld/go-codec-dagpb v1.6.0 // indirect\n\tgithub.com/ipld/go-ipld-prime v0.21.0 // indirect\n\tgithub.com/jackpal/go-nat-pmp v1.0.2 // indirect\n\tgithub.com/jbenet/go-temp-err-catcher v0.1.0 // indirect\n\tgithub.com/jbenet/goprocess v0.1.4 // indirect\n\tgithub.com/kilic/bls12-381 v0.1.1-0.20210503002446-7b7597926c69 // indirect\n\tgithub.com/klauspost/compress v1.17.8 // indirect\n\tgithub.com/klauspost/cpuid/v2 v2.2.7 // indirect\n\tgithub.com/koron/go-ssdp v0.0.4 // indirect\n\tgithub.com/libp2p/go-buffer-pool v0.1.0 // indirect\n\tgithub.com/libp2p/go-cidranger v1.1.0 // indirect\n\tgithub.com/libp2p/go-doh-resolver v0.4.0 // indirect\n\tgithub.com/libp2p/go-flow-metrics v0.1.0 // indirect\n\tgithub.com/libp2p/go-libp2p-asn-util v0.4.1 // indirect\n\tgithub.com/libp2p/go-libp2p-gostream v0.6.0 // indirect\n\tgithub.com/libp2p/go-libp2p-http v0.5.0 // indirect\n\tgithub.com/libp2p/go-libp2p-kbucket v0.6.3 // indirect\n\tgithub.com/libp2p/go-libp2p-pubsub-router v0.6.0 // indirect\n\tgithub.com/libp2p/go-libp2p-record v0.2.0 // indirect\n\tgithub.com/libp2p/go-libp2p-routing-helpers v0.7.3 // indirect\n\tgithub.com/libp2p/go-libp2p-xor v0.1.0 // indirect\n\tgithub.com/libp2p/go-msgio v0.3.0 // indirect\n\tgithub.com/libp2p/go-nat v0.2.0 // indirect\n\tgithub.com/libp2p/go-netroute v0.2.1 // indirect\n\tgithub.com/libp2p/go-reuseport v0.4.0 // indirect\n\tgithub.com/libp2p/go-yamux/v4 v4.0.1 // indirect\n\tgithub.com/libp2p/zeroconf/v2 v2.2.0 // indirect\n\tgithub.com/lyft/protoc-gen-star/v2 v2.0.3 // indirect\n\tgithub.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect\n\tgithub.com/maruel/circular v0.0.0-20200815005550-36e533b830e9 // indirect\n\tgithub.com/mattn/go-colorable v0.1.12 // indirect\n\tgithub.com/mattn/go-isatty v0.0.20 // indirect\n\tgithub.com/mattn/go-runewidth v0.0.8 // indirect\n\tgithub.com/mattn/go-sqlite3 v1.14.16 // indirect\n\tgithub.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect\n\tgithub.com/miekg/dns v1.1.59 // indirect\n\tgithub.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect\n\tgithub.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect\n\tgithub.com/minio/sha256-simd v1.0.1 // indirect\n\tgithub.com/mitchellh/copystructure v1.0.0 // indirect\n\tgithub.com/mitchellh/go-homedir v1.1.0 // indirect\n\tgithub.com/mitchellh/reflectwalk v1.0.1 // indirect\n\tgithub.com/mr-tron/base58 v1.2.0 // indirect\n\tgithub.com/multiformats/go-base32 v0.1.0 // indirect\n\tgithub.com/multiformats/go-base36 v0.2.0 // indirect\n\tgithub.com/multiformats/go-multicodec v0.9.0 // indirect\n\tgithub.com/multiformats/go-multistream v0.5.0 // indirect\n\tgithub.com/multiformats/go-varint v0.0.7 // indirect\n\tgithub.com/mutecomm/go-sqlcipher/v4 v4.4.2 // indirect\n\tgithub.com/mwitkow/go-proto-validators v0.0.0-20180403085117-0950a7990007 // indirect\n\tgithub.com/onsi/ginkgo/v2 v2.17.3 // indirect\n\tgithub.com/opencontainers/runtime-spec v1.2.0 // indirect\n\tgithub.com/opentracing/opentracing-go v1.2.0 // indirect\n\tgithub.com/openzipkin/zipkin-go v0.4.3 // indirect\n\tgithub.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect\n\tgithub.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9 // indirect\n\tgithub.com/peterbourgon/ff/v3 v3.0.0 // indirect\n\tgithub.com/pion/datachannel v1.5.6 // indirect\n\tgithub.com/pion/dtls/v2 v2.2.11 // indirect\n\tgithub.com/pion/ice/v2 v2.3.24 // indirect\n\tgithub.com/pion/interceptor v0.1.29 // indirect\n\tgithub.com/pion/logging v0.2.2 // indirect\n\tgithub.com/pion/mdns v0.0.12 // indirect\n\tgithub.com/pion/randutil v0.1.0 // indirect\n\tgithub.com/pion/rtcp v1.2.14 // indirect\n\tgithub.com/pion/rtp v1.8.6 // indirect\n\tgithub.com/pion/sctp v1.8.16 // indirect\n\tgithub.com/pion/sdp/v3 v3.0.9 // indirect\n\tgithub.com/pion/srtp/v2 v2.0.18 // indirect\n\tgithub.com/pion/stun v0.6.1 // indirect\n\tgithub.com/pion/transport/v2 v2.2.5 // indirect\n\tgithub.com/pion/turn/v2 v2.1.6 // indirect\n\tgithub.com/pion/webrtc/v3 v3.2.40 // indirect\n\tgithub.com/pmezard/go-difflib v1.0.0 // indirect\n\tgithub.com/polydawn/refmt v0.89.0 // indirect\n\tgithub.com/pquerna/cachecontrol v0.1.0 // indirect\n\tgithub.com/prometheus/client_model v0.6.1 // indirect\n\tgithub.com/prometheus/common v0.53.0 // indirect\n\tgithub.com/prometheus/procfs v0.15.0 // indirect\n\tgithub.com/prometheus/statsd_exporter v0.22.7 // indirect\n\tgithub.com/pseudomuto/protokit v0.2.0 // indirect\n\tgithub.com/quic-go/qpack v0.4.0 // indirect\n\tgithub.com/quic-go/quic-go v0.44.0 // indirect\n\tgithub.com/quic-go/webtransport-go v0.8.0 // indirect\n\tgithub.com/raulk/go-watchdog v1.3.0 // indirect\n\tgithub.com/rs/cors v1.10.1 // indirect\n\tgithub.com/samber/lo v1.39.0 // indirect\n\tgithub.com/spaolacci/murmur3 v1.1.0 // indirect\n\tgithub.com/spf13/afero v1.10.0 // indirect\n\tgithub.com/spf13/cobra v1.6.1 // indirect\n\tgithub.com/spf13/pflag v1.0.5 // indirect\n\tgithub.com/square/go-jose/v3 v3.0.0-20200630053402-0a67ce9b0693 // indirect\n\tgithub.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect\n\tgithub.com/teserakt-io/golang-ed25519 v0.0.0-20210104091850-3888c087a4c8 // indirect\n\tgithub.com/ucarion/urlpath v0.0.0-20200424170820-7ccc79b76bbb // indirect\n\tgithub.com/whyrusleeping/base32 v0.0.0-20170828182744-c30ac30633cc // indirect\n\tgithub.com/whyrusleeping/cbor v0.0.0-20171005072247-63513f603b11 // indirect\n\tgithub.com/whyrusleeping/cbor-gen v0.1.1 // indirect\n\tgithub.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f // indirect\n\tgithub.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 // indirect\n\tgithub.com/whyrusleeping/go-sysinfo v0.0.0-20190219211824-4a357d4b90b1 // indirect\n\tgithub.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 // indirect\n\tgithub.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect\n\tgithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect\n\tgithub.com/xeipuuv/gojsonschema v1.2.0 // indirect\n\tgo.opencensus.io v0.24.0 // indirect\n\tgo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0 // indirect\n\tgo.opentelemetry.io/otel v1.26.0 // indirect\n\tgo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.26.0 // indirect\n\tgo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.26.0 // indirect\n\tgo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.26.0 // indirect\n\tgo.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.26.0 // indirect\n\tgo.opentelemetry.io/otel/exporters/zipkin v1.26.0 // indirect\n\tgo.opentelemetry.io/otel/metric v1.26.0 // indirect\n\tgo.opentelemetry.io/otel/sdk v1.26.0 // indirect\n\tgo.opentelemetry.io/otel/trace v1.26.0 // indirect\n\tgo.opentelemetry.io/proto/otlp v1.2.0 // indirect\n\tgo.uber.org/atomic v1.11.0 // indirect\n\tgo.uber.org/dig v1.17.1 // indirect\n\tgo.uber.org/fx v1.21.1 // indirect\n\tgo.uber.org/mock v0.4.0 // indirect\n\tgo4.org v0.0.0-20230225012048-214862532bf5 // indirect\n\tgolang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect\n\tgolang.org/x/mod v0.17.0 // indirect\n\tgolang.org/x/net v0.26.0 // indirect\n\tgolang.org/x/oauth2 v0.20.0 // indirect\n\tgolang.org/x/sync v0.7.0 // indirect\n\tgolang.org/x/sys v0.21.0 // indirect\n\tgolang.org/x/text v0.16.0 // indirect\n\tgonum.org/v1/gonum v0.15.0 // indirect\n\tgoogle.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd // indirect\n\tgopkg.in/square/go-jose.v2 v2.6.0 // indirect\n\tgopkg.in/yaml.v2 v2.4.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n\tlukechampine.com/blake3 v1.3.0 // indirect\n\tmoul.io/banner v1.0.1 // indirect\n\tmoul.io/motd v1.0.0 // indirect\n)\n"
  },
  {
    "path": "go.sum",
    "content": "bazil.org/fuse v0.0.0-20200117225306-7b5117fecadc h1:utDghgcjE8u+EBjHOgYT+dJPcnDF05KqWMBcjuJy510=\nbazil.org/fuse v0.0.0-20200117225306-7b5117fecadc/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM=\nberty.tech/go-ipfs-log v1.10.3-0.20240719141234-29e2d26e2aeb h1:FogPdtHCS/jQMX0iC9r/iQxVyYIFEpcoQvQrf6gxf0w=\nberty.tech/go-ipfs-log v1.10.3-0.20240719141234-29e2d26e2aeb/go.mod h1:9iZx/jWL+Su7j5ALhljjKZN/QScM4ONGs7yqqlcb/Qg=\nberty.tech/go-ipfs-repo-encrypted v1.3.1-0.20240722095251-c6b363b38785 h1:71O7eF4Hr22KKOk5y9Uzvt372siWoA55/DBj2DSbafA=\nberty.tech/go-ipfs-repo-encrypted v1.3.1-0.20240722095251-c6b363b38785/go.mod h1:JjEWS7xkbCQAm2NFt2JKFg3wx2Qkgr2jBMHRAgkfU10=\nberty.tech/go-orbit-db v1.22.2-0.20240719144258-ec7d1faaca68 h1:Y0TmVC11z+CQpQFoqyeOcrh/wgEIhbI/ZppfJOjuvDk=\nberty.tech/go-orbit-db v1.22.2-0.20240719144258-ec7d1faaca68/go.mod h1:UoKTs3bAL3Q1eCwj2VKA/LFspSFbacNeZsUkxDh3S3Q=\ncloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo=\ncloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=\ncloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=\ncloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=\ncloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=\ncloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=\ncloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=\ncloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=\ncloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=\ncloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=\ncloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=\ncloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=\ncloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=\ncloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=\ncloud.google.com/go v0.63.0/go.mod h1:GmezbQc7T2snqkEXWfZ0sy0VfkB/ivI2DdtJL2DEmlg=\ncloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=\ncloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=\ncloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=\ncloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=\ncloud.google.com/go v0.105.0 h1:DNtEKRBAAzeS4KyIory52wWHuClNaXJ5x1F7xa4q+5Y=\ncloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=\ncloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=\ncloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=\ncloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=\ncloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=\ncloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=\ncloud.google.com/go/compute v1.13.0 h1:AYrLkB8NPdDRslNp4Jxmzrhdr03fUAIDbiGFjLWowoU=\ncloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=\ncloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=\ncloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=\ncloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=\ncloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=\ncloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=\ncloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=\ncloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=\ncloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=\ncloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=\ncloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=\ncloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=\ncloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=\ncloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=\ncontrib.go.opencensus.io/exporter/prometheus v0.4.2 h1:sqfsYl5GIY/L570iT+l93ehxaWJs2/OwXtiWwew3oAg=\ncontrib.go.opencensus.io/exporter/prometheus v0.4.2/go.mod h1:dvEHbiKmgvbr5pjaF9fpw1KeYcjrnC1J8B+JKjsZyRQ=\ndmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=\ndmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=\ndmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=\ndmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=\ndmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=\nfilippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek=\nfilippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=\ngit.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=\ngithub.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=\ngithub.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M=\ngithub.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=\ngithub.com/DataDog/zstd v1.4.1 h1:3oxKN3wbHibqx897utPC2LTQU4J+IHWWJO+glkAkpFM=\ngithub.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=\ngithub.com/Jorropo/jsync v1.0.1 h1:6HgRolFZnsdfzRUj+ImB9og1JYOxQoReSywkHOGSaUU=\ngithub.com/Jorropo/jsync v1.0.1/go.mod h1:jCOZj3vrBCri3bSU3ErUYvevKlnbssrXeCivybS5ABQ=\ngithub.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg=\ngithub.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=\ngithub.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=\ngithub.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=\ngithub.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60=\ngithub.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=\ngithub.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=\ngithub.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=\ngithub.com/VictoriaMetrics/fastcache v1.5.7 h1:4y6y0G8PRzszQUYIQHHssv/jgPHAb5qQuuDNdCbyAgw=\ngithub.com/VictoriaMetrics/fastcache v1.5.7/go.mod h1:ptDBkNMQI4RtmVo8VS/XwRY6RoTu1dAWCbrk+6WsEM8=\ngithub.com/aead/ecdh v0.2.0 h1:pYop54xVaq/CEREFEcukHRZfTdjiWvYIsZDXXrBapQQ=\ngithub.com/aead/ecdh v0.2.0/go.mod h1:a9HHtXuSo8J1Js1MwLQx2mBhkXMT6YwUmVVEY4tTB8U=\ngithub.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=\ngithub.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=\ngithub.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=\ngithub.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=\ngithub.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=\ngithub.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=\ngithub.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=\ngithub.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9 h1:ez/4by2iGztzR4L0zgAOR8lTQK9VlyBVVd7G4omaOQs=\ngithub.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=\ngithub.com/alexbrainman/goissue34681 v0.0.0-20191006012335-3fc7a47baff5 h1:iW0a5ljuFxkLGPNem5Ui+KBjFJzKg4Fv2fnxe4dvzpM=\ngithub.com/alexbrainman/goissue34681 v0.0.0-20191006012335-3fc7a47baff5/go.mod h1:Y2QMoi1vgtOIfc+6DhrMOGkLoGzqSV2rKp4Sm+opsyA=\ngithub.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8=\ngithub.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=\ngithub.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=\ngithub.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=\ngithub.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=\ngithub.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=\ngithub.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=\ngithub.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o=\ngithub.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=\ngithub.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=\ngithub.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=\ngithub.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=\ngithub.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=\ngithub.com/berty/emitter-go v0.0.0-20221031144724-5dae963c3622 h1:kJqfCXKR5EJdh9HYh4rjYL3QvxjP5cnCssIU141m79c=\ngithub.com/berty/emitter-go v0.0.0-20221031144724-5dae963c3622/go.mod h1:G66sIy+q6BKIoKoKNqFU7sxSnrS5d8Z8meQ3Iu0ZJ4o=\ngithub.com/berty/go-libp2p-rendezvous v0.5.1 h1:6KnCOlyMIKAZq5COJeglWK5M8MhSJ2cKtMDf6x0KQm0=\ngithub.com/berty/go-libp2p-rendezvous v0.5.1/go.mod h1:gNhPX2RnxaGHLKxj/hWiaQKR2TwYnKxjtFXoVhzUBYE=\ngithub.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=\ngithub.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=\ngithub.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=\ngithub.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI=\ngithub.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=\ngithub.com/btcsuite/btcd v0.22.1 h1:CnwP9LM/M9xuRrGSCGeMVs9iv09uMqwsVX7EeIpgV2c=\ngithub.com/btcsuite/btcd v0.22.1/go.mod h1:wqgTSL29+50LRkmOVknEdmt8ZojIzhuWvgu/iptuN7Y=\ngithub.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U=\ngithub.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=\ngithub.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=\ngithub.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=\ngithub.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce h1:YtWJF7RHm2pYCvA5t0RPmAaLUhREsKuKd+SLhxFbFeQ=\ngithub.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o=\ngithub.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=\ngithub.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=\ngithub.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=\ngithub.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=\ngithub.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=\ngithub.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=\ngithub.com/buicongtan1997/protoc-gen-swagger-config v0.0.0-20200705084907-1342b78c1a7e h1:SHEegopzGCu0vkoUK98733r9TfviEO57VVPkYQ0G+WU=\ngithub.com/buicongtan1997/protoc-gen-swagger-config v0.0.0-20200705084907-1342b78c1a7e/go.mod h1:NQIP/fdybt3yyAPo2/ew6pJYzUb1EWizEblXkQOu+TE=\ngithub.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=\ngithub.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\ngithub.com/ceramicnetwork/go-dag-jose v0.1.0 h1:yJ/HVlfKpnD3LdYP03AHyTvbm3BpPiz2oZiOeReJRdU=\ngithub.com/ceramicnetwork/go-dag-jose v0.1.0/go.mod h1:qYA1nYt0X8u4XoMAVoOV3upUVKtrxy/I670Dg5F0wjI=\ngithub.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=\ngithub.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=\ngithub.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=\ngithub.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/cheggaaa/pb v1.0.29 h1:FckUN5ngEk2LpvuG0fw1GEFx6LtyY2pWI/Z2QgCnEYo=\ngithub.com/cheggaaa/pb v1.0.29/go.mod h1:W40334L7FMC5JKWldsTWbdGjLo0RxUKK73K+TuPxX30=\ngithub.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=\ngithub.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=\ngithub.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=\ngithub.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=\ngithub.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=\ngithub.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=\ngithub.com/containerd/cgroups v0.0.0-20201119153540-4cbc285b3327/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE=\ngithub.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM=\ngithub.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw=\ngithub.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=\ngithub.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=\ngithub.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=\ngithub.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=\ngithub.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=\ngithub.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=\ngithub.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=\ngithub.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=\ngithub.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=\ngithub.com/crackcomm/go-gitignore v0.0.0-20231225121904-e25f5bc08668 h1:ZFUue+PNxmHlu7pYv+IYMtqlaO/0VwaGEqKepZf9JpA=\ngithub.com/crackcomm/go-gitignore v0.0.0-20231225121904-e25f5bc08668/go.mod h1:p1d6YEZWvFzEh4KLyvBcVSnrfNDDvK2zfK/4x2v/4pE=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/cskr/pubsub v1.0.2 h1:vlOzMhl6PFn60gRlTQQsIfVwaPB/B/8MziK8FhEPt/0=\ngithub.com/cskr/pubsub v1.0.2/go.mod h1:/8MzYXk/NJAz782G8RPkFzXTZVu63VotefPnR9TIRis=\ngithub.com/daixiang0/gci v0.8.2 h1:4VLVDNdJ+wkXxz/nr5QRrbeK+JCvkMVqYjUWB5EnPF4=\ngithub.com/daixiang0/gci v0.8.2/go.mod h1:EpVfrztufwVgQRXjnX4zuNinEpLj5OmMjtu/+MB0V0c=\ngithub.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c h1:pFUpOrbxDR6AkioZ1ySsx5yxlDQZ8stG2b88gTPxgJU=\ngithub.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6UhI8N9EjYm1c2odKpFpAYeR8dsBeM7PtzQhRgxRr9U=\ngithub.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y=\ngithub.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=\ngithub.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg=\ngithub.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=\ngithub.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4=\ngithub.com/dgraph-io/badger v1.6.2 h1:mNw0qs90GVgGGWylh0umH5iag1j6n/PeJtNvL6KY/x8=\ngithub.com/dgraph-io/badger v1.6.2/go.mod h1:JW2yswe3V058sS0kZ2h/AXeDSqFjxnZcRrVH//y2UQE=\ngithub.com/dgraph-io/badger/v2 v2.2007.3 h1:Sl9tQWz92WCbVSe8pj04Tkqlm2boW+KAxd+XSs58SQI=\ngithub.com/dgraph-io/badger/v2 v2.2007.3/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE=\ngithub.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=\ngithub.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=\ngithub.com/dgraph-io/ristretto v0.0.3 h1:jh22xisGBjrEVnRZ1DVTpBVQm0Xndu8sMl0CWDzSIBI=\ngithub.com/dgraph-io/ristretto v0.0.3/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=\ngithub.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=\ngithub.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=\ngithub.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=\ngithub.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=\ngithub.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=\ngithub.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=\ngithub.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=\ngithub.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=\ngithub.com/eclipse/paho.mqtt.golang v1.4.2 h1:66wOzfUHSSI1zamx7jR6yMEI5EuHnT1G6rNA5PM12m4=\ngithub.com/eclipse/paho.mqtt.golang v1.4.2/go.mod h1:JGt0RsEwEX+Xa/agj90YJ9d9DH2b7upDZMK9HRbFvCA=\ngithub.com/elastic/gosigar v0.12.0/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs=\ngithub.com/elastic/gosigar v0.14.2 h1:Dg80n8cr90OZ7x+bAax/QjoW/XqTI11RmA79ZwIm9/4=\ngithub.com/elastic/gosigar v0.14.2/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs=\ngithub.com/elgris/jsondiff v0.0.0-20160530203242-765b5c24c302 h1:QV0ZrfBLpFc2KDk+a4LJefDczXnonRwrYrQJY/9L4dA=\ngithub.com/elgris/jsondiff v0.0.0-20160530203242-765b5c24c302/go.mod h1:qBlWZqWeVx9BjvqBsnC/8RUlAYpIFmPvgROcw0n1scE=\ngithub.com/emicklei/proto v1.6.13 h1:8iuAuKbFmFhkmstObb0EV/Hrn9W+x6EuV1y5Da8Ye9E=\ngithub.com/emicklei/proto v1.6.13/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=\ngithub.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=\ngithub.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=\ngithub.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A=\ngithub.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew=\ngithub.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5 h1:BBso6MBKW8ncyZLv37o+KNyy0HrrHgfnOaGQC2qvN+A=\ngithub.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5/go.mod h1:JpoxHjuQauoxiFMl1ie8Xc/7TfLuMZ5eOCONd1sUBHg=\ngithub.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=\ngithub.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=\ngithub.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=\ngithub.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=\ngithub.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=\ngithub.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=\ngithub.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=\ngithub.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=\ngithub.com/flynn/noise v1.1.0 h1:KjPQoQCEFdZDiP03phOvGi11+SVVhBG2wOWAorLsstg=\ngithub.com/flynn/noise v1.1.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag=\ngithub.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk=\ngithub.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=\ngithub.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=\ngithub.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og=\ngithub.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=\ngithub.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=\ngithub.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=\ngithub.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=\ngithub.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=\ngithub.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=\ngithub.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=\ngithub.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=\ngithub.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=\ngithub.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=\ngithub.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=\ngithub.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=\ngithub.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=\ngithub.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=\ngithub.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=\ngithub.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=\ngithub.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=\ngithub.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=\ngithub.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU=\ngithub.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=\ngithub.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=\ngithub.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=\ngithub.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=\ngithub.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA=\ngithub.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=\ngithub.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=\ngithub.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=\ngithub.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=\ngithub.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=\ngithub.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=\ngithub.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=\ngithub.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=\ngithub.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0=\ngithub.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=\ngithub.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=\ngithub.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=\ngithub.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=\ngithub.com/gofrs/uuid v4.3.1+incompatible h1:0/KbAdpx3UXAx1kEOWHJeOkpbgRFGHVgv+CFIY7dBJI=\ngithub.com/gofrs/uuid v4.3.1+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=\ngithub.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=\ngithub.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=\ngithub.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=\ngithub.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=\ngithub.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=\ngithub.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4=\ngithub.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=\ngithub.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=\ngithub.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=\ngithub.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=\ngithub.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=\ngithub.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=\ngithub.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=\ngithub.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=\ngithub.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=\ngithub.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=\ngithub.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=\ngithub.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=\ngithub.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=\ngithub.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=\ngithub.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=\ngithub.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=\ngithub.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngithub.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=\ngithub.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngithub.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=\ngithub.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=\ngithub.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=\ngithub.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=\ngithub.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=\ngithub.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=\ngithub.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20240509144519-723abb6459b7 h1:velgFPYr1X9TDwLIfkV7fWqsFlf7TeP11M/7kPd/dVI=\ngithub.com/google/pprof v0.0.0-20240509144519-723abb6459b7/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=\ngithub.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=\ngithub.com/google/tink/go v1.7.0 h1:6Eox8zONGebBFcCBqkVmt60LaWZa6xg1cl/DwAh/J1w=\ngithub.com/google/tink/go v1.7.0/go.mod h1:GAUOd+QE3pgj9q8VKIGTCP33c/B7eb4NhxLcgTJZStM=\ngithub.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=\ngithub.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=\ngithub.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=\ngithub.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=\ngithub.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=\ngithub.com/gopherjs/gopherjs v0.0.0-20190812055157-5d271430af9f h1:KMlcu9X58lhTA/KrfX8Bi1LQSO4pzoVjTiL3h4Jk+Zk=\ngithub.com/gopherjs/gopherjs v0.0.0-20190812055157-5d271430af9f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=\ngithub.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=\ngithub.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=\ngithub.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=\ngithub.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=\ngithub.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=\ngithub.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI=\ngithub.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8=\ngithub.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=\ngithub.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=\ngithub.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=\ngithub.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0=\ngithub.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=\ngithub.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU=\ngithub.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48=\ngithub.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=\ngithub.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=\ngithub.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=\ngithub.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=\ngithub.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=\ngithub.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c=\ngithub.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=\ngithub.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=\ngithub.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=\ngithub.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=\ngithub.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=\ngithub.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=\ngithub.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=\ngithub.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw=\ngithub.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=\ngithub.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc=\ngithub.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8=\ngithub.com/hyperledger/aries-framework-go v0.1.9-0.20221202141134-083803ecf0a3 h1:r/jf1DTXG72uO5xK1VgZGywcjBlXh1E8kKnAt67bXJA=\ngithub.com/hyperledger/aries-framework-go v0.1.9-0.20221202141134-083803ecf0a3/go.mod h1:5lp5+NPjRngsjFLYYGg5mtkvw6I4Mr7CKz+wHYxROk0=\ngithub.com/hyperledger/aries-framework-go/spi v0.0.0-20221025204933-b807371b6f1e h1:SxbXlF39661T9w/L9PhVdtbJfJ51Pm4JYEEW6XfZHEQ=\ngithub.com/hyperledger/aries-framework-go/spi v0.0.0-20221025204933-b807371b6f1e/go.mod h1:oryUyWb23l/a3tAP9KW+GBbfcfqp9tZD4y5hSkFrkqI=\ngithub.com/hyperledger/ursa-wrapper-go v0.3.1 h1:Do+QrVNniY77YK2jTIcyWqj9rm/Yb5SScN0bqCjiibA=\ngithub.com/hyperledger/ursa-wrapper-go v0.3.1/go.mod h1:nPSAuMasIzSVciQo22PedBk4Opph6bJ6ia3ms7BH/mk=\ngithub.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=\ngithub.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=\ngithub.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA=\ngithub.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=\ngithub.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=\ngithub.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=\ngithub.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=\ngithub.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=\ngithub.com/ipfs-shipyard/nopfs v0.0.12 h1:mvwaoefDF5VI9jyvgWCmaoTJIJFAfrbyQV5fJz35hlk=\ngithub.com/ipfs-shipyard/nopfs v0.0.12/go.mod h1:mQyd0BElYI2gB/kq/Oue97obP4B3os4eBmgfPZ+hnrE=\ngithub.com/ipfs-shipyard/nopfs/ipfs v0.13.2-0.20231027223058-cde3b5ba964c h1:7UynTbtdlt+w08ggb1UGLGaGjp1mMaZhoTZSctpn5Ak=\ngithub.com/ipfs-shipyard/nopfs/ipfs v0.13.2-0.20231027223058-cde3b5ba964c/go.mod h1:6EekK/jo+TynwSE/ZOiOJd4eEvRXoavEC3vquKtv4yI=\ngithub.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs=\ngithub.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0=\ngithub.com/ipfs/boxo v0.20.0 h1:umUl7q1v5g5AX8FPLTnZBvvagLmT+V0Tt61EigP81ec=\ngithub.com/ipfs/boxo v0.20.0/go.mod h1:mwttn53Eibgska2DhVIj7ln3UViq7MVHRxOMb+ehSDM=\ngithub.com/ipfs/go-bitfield v1.1.0 h1:fh7FIo8bSwaJEh6DdTWbCeZ1eqOaOkKFI74SCnsWbGA=\ngithub.com/ipfs/go-bitfield v1.1.0/go.mod h1:paqf1wjq/D2BBmzfTVFlJQ9IlFOZpg422HL0HqsGWHU=\ngithub.com/ipfs/go-bitswap v0.11.0 h1:j1WVvhDX1yhG32NTC9xfxnqycqYIlhzEzLXG/cU1HyQ=\ngithub.com/ipfs/go-bitswap v0.11.0/go.mod h1:05aE8H3XOU+LXpTedeAS0OZpcO1WFsj5niYQH9a1Tmk=\ngithub.com/ipfs/go-block-format v0.0.3/go.mod h1:4LmD4ZUw0mhO+JSKdpWwrzATiEfM7WWgQ8H5l6P8MVk=\ngithub.com/ipfs/go-block-format v0.2.0 h1:ZqrkxBA2ICbDRbK8KJs/u0O3dlp6gmAuuXUJNiW1Ycs=\ngithub.com/ipfs/go-block-format v0.2.0/go.mod h1:+jpL11nFx5A/SPpsoBn6Bzkra/zaArfSmsknbPMYgzM=\ngithub.com/ipfs/go-blockservice v0.5.2 h1:in9Bc+QcXwd1apOVM7Un9t8tixPKdaHQFdLSUM1Xgk8=\ngithub.com/ipfs/go-blockservice v0.5.2/go.mod h1:VpMblFEqG67A/H2sHKAemeH9vlURVavlysbdUI632yk=\ngithub.com/ipfs/go-cid v0.0.3/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=\ngithub.com/ipfs/go-cid v0.0.4/go.mod h1:4LLaPOQwmk5z9LBgQnpkivrx8BJjUyGwTXCd5Xfj6+M=\ngithub.com/ipfs/go-cid v0.0.7/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I=\ngithub.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s=\ngithub.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk=\ngithub.com/ipfs/go-cidutil v0.1.0 h1:RW5hO7Vcf16dplUU60Hs0AKDkQAVPVplr7lk97CFL+Q=\ngithub.com/ipfs/go-cidutil v0.1.0/go.mod h1:e7OEVBMIv9JaOxt9zaGEmAoSlXW9jdFZ5lP/0PwcfpA=\ngithub.com/ipfs/go-datastore v0.1.0/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE=\ngithub.com/ipfs/go-datastore v0.1.1/go.mod h1:w38XXW9kVFNp57Zj5knbKWM2T+KOZCGDRVNdgPHtbHw=\ngithub.com/ipfs/go-datastore v0.5.0/go.mod h1:9zhEApYMTl17C8YDp7JmU7sQZi2/wqiYh73hakZ90Bk=\ngithub.com/ipfs/go-datastore v0.5.1/go.mod h1:9zhEApYMTl17C8YDp7JmU7sQZi2/wqiYh73hakZ90Bk=\ngithub.com/ipfs/go-datastore v0.6.0 h1:JKyz+Gvz1QEZw0LsX1IBn+JFCJQH4SJVFtM4uWU0Myk=\ngithub.com/ipfs/go-datastore v0.6.0/go.mod h1:rt5M3nNbSO/8q1t4LNkLyUwRs8HupMeN/8O4Vn9YAT8=\ngithub.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk=\ngithub.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps=\ngithub.com/ipfs/go-ds-badger v0.0.7/go.mod h1:qt0/fWzZDoPW6jpQeqUjR5kBfhDNB65jd9YlmAvpQBk=\ngithub.com/ipfs/go-ds-badger v0.3.0 h1:xREL3V0EH9S219kFFueOYJJTcjgNSZ2HY1iSvN7U1Ro=\ngithub.com/ipfs/go-ds-badger v0.3.0/go.mod h1:1ke6mXNqeV8K3y5Ak2bAA0osoTfmxUdupVCGm4QUIek=\ngithub.com/ipfs/go-ds-badger2 v0.1.3 h1:Zo9JicXJ1DmXTN4KOw7oPXkspZ0AWHcAFCP1tQKnegg=\ngithub.com/ipfs/go-ds-badger2 v0.1.3/go.mod h1:TPhhljfrgewjbtuL/tczP8dNrBYwwk+SdPYbms/NO9w=\ngithub.com/ipfs/go-ds-flatfs v0.5.1 h1:ZCIO/kQOS/PSh3vcF1H6a8fkRGS7pOfwfPdx4n/KJH4=\ngithub.com/ipfs/go-ds-flatfs v0.5.1/go.mod h1:RWTV7oZD/yZYBKdbVIFXTX2fdY2Tbvl94NsWqmoyAX4=\ngithub.com/ipfs/go-ds-leveldb v0.1.0/go.mod h1:hqAW8y4bwX5LWcCtku2rFNX3vjDZCy5LZCg+cSZvYb8=\ngithub.com/ipfs/go-ds-leveldb v0.5.0 h1:s++MEBbD3ZKc9/8/njrn4flZLnCuY9I79v94gBUNumo=\ngithub.com/ipfs/go-ds-leveldb v0.5.0/go.mod h1:d3XG9RUDzQ6V4SHi8+Xgj9j1XuEk1z82lquxrVbml/Q=\ngithub.com/ipfs/go-ds-measure v0.2.0 h1:sG4goQe0KDTccHMyT45CY1XyUbxe5VwTKpg2LjApYyQ=\ngithub.com/ipfs/go-ds-measure v0.2.0/go.mod h1:SEUD/rE2PwRa4IQEC5FuNAmjJCyYObZr9UvVh8V3JxE=\ngithub.com/ipfs/go-ds-sql v0.3.0 h1:PLBbl0Rt0tBwWhQ0b3GCQbH+Bgd6aj2srKG6vJ7nYl4=\ngithub.com/ipfs/go-ds-sql v0.3.0/go.mod h1:jE3bhmuUnMPXFftc4NEAiPUfgiwiv7fIdjozuX+m1/E=\ngithub.com/ipfs/go-fs-lock v0.0.7 h1:6BR3dajORFrFTkb5EpCUFIAypsoxpGpDSVUdFwzgL9U=\ngithub.com/ipfs/go-fs-lock v0.0.7/go.mod h1:Js8ka+FNYmgQRLrRXzU3CB/+Csr1BwrRilEcvYrHhhc=\ngithub.com/ipfs/go-ipfs-blockstore v1.3.1 h1:cEI9ci7V0sRNivqaOr0elDsamxXFxJMMMy7PTTDQNsQ=\ngithub.com/ipfs/go-ipfs-blockstore v1.3.1/go.mod h1:KgtZyc9fq+P2xJUiCAzbRdhhqJHvsw8u2Dlqy2MyRTE=\ngithub.com/ipfs/go-ipfs-blocksutil v0.0.1 h1:Eh/H4pc1hsvhzsQoMEP3Bke/aW5P5rVM1IWFJMcGIPQ=\ngithub.com/ipfs/go-ipfs-blocksutil v0.0.1/go.mod h1:Yq4M86uIOmxmGPUHv/uI7uKqZNtLb449gwKqXjIsnRk=\ngithub.com/ipfs/go-ipfs-chunker v0.0.5 h1:ojCf7HV/m+uS2vhUGWcogIIxiO5ubl5O57Q7NapWLY8=\ngithub.com/ipfs/go-ipfs-chunker v0.0.5/go.mod h1:jhgdF8vxRHycr00k13FM8Y0E+6BoalYeobXmUyTreP8=\ngithub.com/ipfs/go-ipfs-cmds v0.11.0 h1:6AsTKwbVxwzrOkq2x89e6jYMGxzYqjt/WbAam69HZQE=\ngithub.com/ipfs/go-ipfs-cmds v0.11.0/go.mod h1:DHp7YfJlOK+2IS07nk+hFmbKHK52tc29W38CaAgWHpk=\ngithub.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw=\ngithub.com/ipfs/go-ipfs-delay v0.0.1 h1:r/UXYyRcddO6thwOnhiznIAiSvxMECGgtv35Xs1IeRQ=\ngithub.com/ipfs/go-ipfs-delay v0.0.1/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw=\ngithub.com/ipfs/go-ipfs-ds-help v1.1.1 h1:B5UJOH52IbcfS56+Ul+sv8jnIV10lbjLF5eOO0C66Nw=\ngithub.com/ipfs/go-ipfs-ds-help v1.1.1/go.mod h1:75vrVCkSdSFidJscs8n4W+77AtTpCIAdDGAwjitJMIo=\ngithub.com/ipfs/go-ipfs-exchange-interface v0.2.1 h1:jMzo2VhLKSHbVe+mHNzYgs95n0+t0Q69GQ5WhRDZV/s=\ngithub.com/ipfs/go-ipfs-exchange-interface v0.2.1/go.mod h1:MUsYn6rKbG6CTtsDp+lKJPmVt3ZrCViNyH3rfPGsZ2E=\ngithub.com/ipfs/go-ipfs-exchange-offline v0.3.0 h1:c/Dg8GDPzixGd0MC8Jh6mjOwU57uYokgWRFidfvEkuA=\ngithub.com/ipfs/go-ipfs-exchange-offline v0.3.0/go.mod h1:MOdJ9DChbb5u37M1IcbrRB02e++Z7521fMxqCNRrz9s=\ngithub.com/ipfs/go-ipfs-keystore v0.1.0 h1:gfuQUO/cyGZgZIHE6OrJas4OnwuxXCqJG7tI0lrB5Qc=\ngithub.com/ipfs/go-ipfs-keystore v0.1.0/go.mod h1:LvLw7Qhnb0RlMOfCzK6OmyWxICip6lQ06CCmdbee75U=\ngithub.com/ipfs/go-ipfs-pq v0.0.3 h1:YpoHVJB+jzK15mr/xsWC574tyDLkezVrDNeaalQBsTE=\ngithub.com/ipfs/go-ipfs-pq v0.0.3/go.mod h1:btNw5hsHBpRcSSgZtiNm/SLj5gYIZ18AKtv3kERkRb4=\ngithub.com/ipfs/go-ipfs-redirects-file v0.1.1 h1:Io++k0Vf/wK+tfnhEh63Yte1oQK5VGT2hIEYpD0Rzx8=\ngithub.com/ipfs/go-ipfs-redirects-file v0.1.1/go.mod h1:tAwRjCV0RjLTjH8DR/AU7VYvfQECg+lpUy2Mdzv7gyk=\ngithub.com/ipfs/go-ipfs-routing v0.3.0 h1:9W/W3N+g+y4ZDeffSgqhgo7BsBSJwPMcyssET9OWevc=\ngithub.com/ipfs/go-ipfs-routing v0.3.0/go.mod h1:dKqtTFIql7e1zYsEuWLyuOU+E0WJWW8JjbTPLParDWo=\ngithub.com/ipfs/go-ipfs-util v0.0.1/go.mod h1:spsl5z8KUnrve+73pOhSVZND1SIxPW5RyBCNzQxlJBc=\ngithub.com/ipfs/go-ipfs-util v0.0.2/go.mod h1:CbPtkWJzjLdEcezDns2XYaehFVNXG9zrdrtMecczcsQ=\ngithub.com/ipfs/go-ipfs-util v0.0.3 h1:2RFdGez6bu2ZlZdI+rWfIdbQb1KudQp3VGwPtdNCmE0=\ngithub.com/ipfs/go-ipfs-util v0.0.3/go.mod h1:LHzG1a0Ig4G+iZ26UUOMjHd+lfM84LZCrn17xAKWBvs=\ngithub.com/ipfs/go-ipld-cbor v0.1.0 h1:dx0nS0kILVivGhfWuB6dUpMa/LAwElHPw1yOGYopoYs=\ngithub.com/ipfs/go-ipld-cbor v0.1.0/go.mod h1:U2aYlmVrJr2wsUBU67K4KgepApSZddGRDWBYR0H4sCk=\ngithub.com/ipfs/go-ipld-format v0.6.0 h1:VEJlA2kQ3LqFSIm5Vu6eIlSxD/Ze90xtc4Meten1F5U=\ngithub.com/ipfs/go-ipld-format v0.6.0/go.mod h1:g4QVMTn3marU3qXchwjpKPKgJv+zF+OlaKMyhJ4LHPg=\ngithub.com/ipfs/go-ipld-git v0.1.1 h1:TWGnZjS0htmEmlMFEkA3ogrNCqWjIxwr16x1OsdhG+Y=\ngithub.com/ipfs/go-ipld-git v0.1.1/go.mod h1:+VyMqF5lMcJh4rwEppV0e6g4nCCHXThLYYDpKUkJubI=\ngithub.com/ipfs/go-ipld-legacy v0.2.1 h1:mDFtrBpmU7b//LzLSypVrXsD8QxkEWxu5qVxN99/+tk=\ngithub.com/ipfs/go-ipld-legacy v0.2.1/go.mod h1:782MOUghNzMO2DER0FlBR94mllfdCJCkTtDtPM51otM=\ngithub.com/ipfs/go-libipfs v0.6.2 h1:QUf3kS3RrCjgtE0QW2d18PFFfOLeEt24Ft892ipLzRI=\ngithub.com/ipfs/go-libipfs v0.6.2/go.mod h1:FmhKgxMOQA572TK5DA3MZ5GL44ZqsMHIrkgK4gLn4A8=\ngithub.com/ipfs/go-log v0.0.1/go.mod h1:kL1d2/hzSpI0thNYjiKfjanbVNU+IIGA/WnNESY9leM=\ngithub.com/ipfs/go-log v1.0.3/go.mod h1:OsLySYkwIbiSUR/yBTdv1qPtcE4FW3WPWk/ewz9Ru+A=\ngithub.com/ipfs/go-log v1.0.5 h1:2dOuUCB1Z7uoczMWgAyDck5JLb72zHzrMnGnCNNbvY8=\ngithub.com/ipfs/go-log v1.0.5/go.mod h1:j0b8ZoR+7+R99LD9jZ6+AJsrzkPbSXbZfGakb5JPtIo=\ngithub.com/ipfs/go-log/v2 v2.0.3/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0=\ngithub.com/ipfs/go-log/v2 v2.0.5/go.mod h1:eZs4Xt4ZUJQFM3DlanGhy7TkwwawCZcSByscwkWG+dw=\ngithub.com/ipfs/go-log/v2 v2.1.3/go.mod h1:/8d0SH3Su5Ooc31QlL1WysJhvyOTDCjcCZ9Axpmri6g=\ngithub.com/ipfs/go-log/v2 v2.3.0/go.mod h1:QqGoj30OTpnKaG/LKTGTxoP2mmQtjVMEnK72gynbe/g=\ngithub.com/ipfs/go-log/v2 v2.5.0/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI=\ngithub.com/ipfs/go-log/v2 v2.5.1 h1:1XdUzF7048prq4aBjDQQ4SL5RxftpRGdXhNRwKSAlcY=\ngithub.com/ipfs/go-log/v2 v2.5.1/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI=\ngithub.com/ipfs/go-merkledag v0.11.0 h1:DgzwK5hprESOzS4O1t/wi6JDpyVQdvm9Bs59N/jqfBY=\ngithub.com/ipfs/go-merkledag v0.11.0/go.mod h1:Q4f/1ezvBiJV0YCIXvt51W/9/kqJGH4I1LsA7+djsM4=\ngithub.com/ipfs/go-metrics-interface v0.0.1 h1:j+cpbjYvu4R8zbleSs36gvB7jR+wsL2fGD6n0jO4kdg=\ngithub.com/ipfs/go-metrics-interface v0.0.1/go.mod h1:6s6euYU4zowdslK0GKHmqaIZ3j/b/tL7HTWtJ4VPgWY=\ngithub.com/ipfs/go-peertaskqueue v0.8.1 h1:YhxAs1+wxb5jk7RvS0LHdyiILpNmRIRnZVztekOF0pg=\ngithub.com/ipfs/go-peertaskqueue v0.8.1/go.mod h1:Oxxd3eaK279FxeydSPPVGHzbwVeHjatZ2GA8XD+KbPU=\ngithub.com/ipfs/go-unixfs v0.4.5 h1:wj8JhxvV1G6CD7swACwSKYa+NgtdWC1RUit+gFnymDU=\ngithub.com/ipfs/go-unixfs v0.4.5/go.mod h1:BIznJNvt/gEx/ooRMI4Us9K8+qeGO7vx1ohnbk8gjFg=\ngithub.com/ipfs/go-unixfsnode v1.9.0 h1:ubEhQhr22sPAKO2DNsyVBW7YB/zA8Zkif25aBvz8rc8=\ngithub.com/ipfs/go-unixfsnode v1.9.0/go.mod h1:HxRu9HYHOjK6HUqFBAi++7DVoWAHn0o4v/nZ/VA+0g8=\ngithub.com/ipfs/go-verifcid v0.0.3 h1:gmRKccqhWDocCRkC+a59g5QW7uJw5bpX9HWBevXa0zs=\ngithub.com/ipfs/go-verifcid v0.0.3/go.mod h1:gcCtGniVzelKrbk9ooUSX/pM3xlH73fZZJDzQJRvOUw=\ngithub.com/ipfs/kubo v0.29.0 h1:J5G5le0/gYkx8qLN/zxDl0LcEXKbHZyMh4FCuQN1nVo=\ngithub.com/ipfs/kubo v0.29.0/go.mod h1:mLhuve/44BxEX5ujEihviRXiaxdlrja3kjJgEs2WhK0=\ngithub.com/ipld/go-car v0.6.2 h1:Hlnl3Awgnq8icK+ze3iRghk805lu8YNq3wlREDTF2qc=\ngithub.com/ipld/go-car v0.6.2/go.mod h1:oEGXdwp6bmxJCZ+rARSkDliTeYnVzv3++eXajZ+Bmr8=\ngithub.com/ipld/go-car/v2 v2.13.1 h1:KnlrKvEPEzr5IZHKTXLAEub+tPrzeAFQVRlSQvuxBO4=\ngithub.com/ipld/go-car/v2 v2.13.1/go.mod h1:QkdjjFNGit2GIkpQ953KBwowuoukoM75nP/JI1iDJdo=\ngithub.com/ipld/go-codec-dagpb v1.6.0 h1:9nYazfyu9B1p3NAgfVdpRco3Fs2nFC72DqVsMj6rOcc=\ngithub.com/ipld/go-codec-dagpb v1.6.0/go.mod h1:ANzFhfP2uMJxRBr8CE+WQWs5UsNa0pYtmKZ+agnUw9s=\ngithub.com/ipld/go-ipld-prime v0.11.0/go.mod h1:+WIAkokurHmZ/KwzDOMUuoeJgaRQktHtEaLglS3ZeV8=\ngithub.com/ipld/go-ipld-prime v0.14.1/go.mod h1:QcE4Y9n/ZZr8Ijg5bGPT0GqYWgZ1704nH0RDcQtgTP0=\ngithub.com/ipld/go-ipld-prime v0.21.0 h1:n4JmcpOlPDIxBcY037SVfpd1G+Sj1nKZah0m6QH9C2E=\ngithub.com/ipld/go-ipld-prime v0.21.0/go.mod h1:3RLqy//ERg/y5oShXXdx5YIp50cFGOanyMctpPjsvxQ=\ngithub.com/ipld/go-ipld-prime/storage/bsadapter v0.0.0-20230102063945-1a409dc236dd h1:gMlw/MhNr2Wtp5RwGdsW23cs+yCuj9k2ON7i9MiJlRo=\ngithub.com/ipld/go-ipld-prime/storage/bsadapter v0.0.0-20230102063945-1a409dc236dd/go.mod h1:wZ8hH8UxeryOs4kJEJaiui/s00hDSbE37OKsL47g+Sw=\ngithub.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=\ngithub.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=\ngithub.com/jbenet/go-cienv v0.1.0 h1:Vc/s0QbQtoxX8MwwSLWWh+xNNZvM3Lw7NsTcHrvvhMc=\ngithub.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA=\ngithub.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABoLk/+KKHggpk=\ngithub.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk=\ngithub.com/jbenet/goprocess v0.0.0-20160826012719-b497e2f366b8/go.mod h1:Ly/wlsjFq/qrU3Rar62tu1gASgGw6chQbSh/XgIIXCY=\ngithub.com/jbenet/goprocess v0.1.3/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4=\ngithub.com/jbenet/goprocess v0.1.4 h1:DRGOFReOMqqDNXwW70QkacFW0YN9QnwLV0Vqk+3oU0o=\ngithub.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4=\ngithub.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=\ngithub.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=\ngithub.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=\ngithub.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a h1:zPPuIq2jAWWPTrGt70eK/BSch+gFAGrNzecsoENgu2o=\ngithub.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a/go.mod h1:yL958EeXv8Ylng6IfnvG4oflryUi3vgA3xPs9hmII1s=\ngithub.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=\ngithub.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=\ngithub.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\ngithub.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=\ngithub.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=\ngithub.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=\ngithub.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=\ngithub.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=\ngithub.com/juju/fslock v0.0.0-20160525022230-4d5c94c67b4b h1:FQ7+9fxhyp82ks9vAuyPzG0/vVbWwMwLJ+P6yJI5FN8=\ngithub.com/juju/fslock v0.0.0-20160525022230-4d5c94c67b4b/go.mod h1:HMcgvsgd0Fjj4XXDkbjdmlbI505rUPBs6WBMYg2pXks=\ngithub.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=\ngithub.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=\ngithub.com/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d/go.mod h1:P2viExyCEfeWGU259JnaQ34Inuec4R38JCyBx2edgD0=\ngithub.com/kilic/bls12-381 v0.1.1-0.20210503002446-7b7597926c69 h1:kMJlf8z8wUcpyI+FQJIdGjAhfTww1y0AbQEv86bpVQI=\ngithub.com/kilic/bls12-381 v0.1.1-0.20210503002446-7b7597926c69/go.mod h1:tlkavyke+Ac7h8R3gZIjI5LKBcvMlSWnXNMgT3vZXo8=\ngithub.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=\ngithub.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=\ngithub.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=\ngithub.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=\ngithub.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=\ngithub.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=\ngithub.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=\ngithub.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=\ngithub.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/koron/go-ssdp v0.0.4 h1:1IDwrghSKYM7yLf7XCzbByg2sJ/JcNOZRXS2jczTwz0=\ngithub.com/koron/go-ssdp v0.0.4/go.mod h1:oDXq+E5IL5q0U8uSBcoAXzTzInwy5lEgC91HoKtbmZk=\ngithub.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=\ngithub.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU=\ngithub.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=\ngithub.com/libp2p/go-buffer-pool v0.0.1/go.mod h1:xtyIz9PMobb13WaxR6Zo1Pd1zXJKYg0a8KiIvDp3TzQ=\ngithub.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM=\ngithub.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8=\ngithub.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg=\ngithub.com/libp2p/go-cidranger v1.1.0 h1:ewPN8EZ0dd1LSnrtuwd4709PXVcITVeuwbag38yPW7c=\ngithub.com/libp2p/go-cidranger v1.1.0/go.mod h1:KWZTfSr+r9qEo9OkI9/SIEeAtw+NNoU0dXIXt15Okic=\ngithub.com/libp2p/go-doh-resolver v0.4.0 h1:gUBa1f1XsPwtpE1du0O+nnZCUqtG7oYi7Bb+0S7FQqw=\ngithub.com/libp2p/go-doh-resolver v0.4.0/go.mod h1:v1/jwsFusgsWIGX/c6vCRrnJ60x7bhTiq/fs2qt0cAg=\ngithub.com/libp2p/go-flow-metrics v0.0.1/go.mod h1:Iv1GH0sG8DtYN3SVJ2eG221wMiNpZxBdp967ls1g+k8=\ngithub.com/libp2p/go-flow-metrics v0.0.3/go.mod h1:HeoSNUrOJVK1jEpDqVEiUOIXqhbnS27omG0uWU5slZs=\ngithub.com/libp2p/go-flow-metrics v0.1.0 h1:0iPhMI8PskQwzh57jB9WxIuIOQ0r+15PChFGkx3Q3WM=\ngithub.com/libp2p/go-flow-metrics v0.1.0/go.mod h1:4Xi8MX8wj5aWNDAZttg6UPmc0ZrnFNsMtpsYUClFtro=\ngithub.com/libp2p/go-libp2p v0.34.1 h1:fxn9vyLo7vJcXQRNvdRbyPjbzuQgi2UiqC8hEbn8a18=\ngithub.com/libp2p/go-libp2p v0.34.1/go.mod h1:snyJQix4ET6Tj+LeI0VPjjxTtdWpeOhYt5lEY0KirkQ=\ngithub.com/libp2p/go-libp2p-asn-util v0.4.1 h1:xqL7++IKD9TBFMgnLPZR6/6iYhawHKHl950SO9L6n94=\ngithub.com/libp2p/go-libp2p-asn-util v0.4.1/go.mod h1:d/NI6XZ9qxw67b4e+NgpQexCIiFYJjErASrYW4PFDN8=\ngithub.com/libp2p/go-libp2p-core v0.2.4/go.mod h1:STh4fdfa5vDYr0/SzYYeqnt+E6KfEV5VxfIrm0bcI0g=\ngithub.com/libp2p/go-libp2p-core v0.3.0/go.mod h1:ACp3DmS3/N64c2jDzcV429ukDpicbL6+TrrxANBjPGw=\ngithub.com/libp2p/go-libp2p-gostream v0.6.0 h1:QfAiWeQRce6pqnYfmIVWJFXNdDyfiR/qkCnjyaZUPYU=\ngithub.com/libp2p/go-libp2p-gostream v0.6.0/go.mod h1:Nywu0gYZwfj7Jc91PQvbGU8dIpqbQQkjWgDuOrFaRdA=\ngithub.com/libp2p/go-libp2p-http v0.5.0 h1:+x0AbLaUuLBArHubbbNRTsgWz0RjNTy6DJLOxQ3/QBc=\ngithub.com/libp2p/go-libp2p-http v0.5.0/go.mod h1:glh87nZ35XCQyFsdzZps6+F4HYI6DctVFY5u1fehwSg=\ngithub.com/libp2p/go-libp2p-kad-dht v0.25.2 h1:FOIk9gHoe4YRWXTu8SY9Z1d0RILol0TrtApsMDPjAVQ=\ngithub.com/libp2p/go-libp2p-kad-dht v0.25.2/go.mod h1:6za56ncRHYXX4Nc2vn8z7CZK0P4QiMcrn77acKLM2Oo=\ngithub.com/libp2p/go-libp2p-kbucket v0.3.1/go.mod h1:oyjT5O7tS9CQurok++ERgc46YLwEpuGoFq9ubvoUOio=\ngithub.com/libp2p/go-libp2p-kbucket v0.6.3 h1:p507271wWzpy2f1XxPzCQG9NiN6R6lHL9GiSErbQQo0=\ngithub.com/libp2p/go-libp2p-kbucket v0.6.3/go.mod h1:RCseT7AH6eJWxxk2ol03xtP9pEHetYSPXOaJnOiD8i0=\ngithub.com/libp2p/go-libp2p-peerstore v0.1.4/go.mod h1:+4BDbDiiKf4PzpANZDAT+knVdLxvqh7hXOujessqdzs=\ngithub.com/libp2p/go-libp2p-pubsub v0.11.1-0.20240711152552-e508d8643ddb h1:Ux/fNS52HowibmSbtEDzeKiu4N5gFklPv/1myFh/VVE=\ngithub.com/libp2p/go-libp2p-pubsub v0.11.1-0.20240711152552-e508d8643ddb/go.mod h1:QEb+hEV9WL9wCiUAnpY29FZR6W3zK8qYlaml8R4q6gQ=\ngithub.com/libp2p/go-libp2p-pubsub-router v0.6.0 h1:D30iKdlqDt5ZmLEYhHELCMRj8b4sFAqrUcshIUvVP/s=\ngithub.com/libp2p/go-libp2p-pubsub-router v0.6.0/go.mod h1:FY/q0/RBTKsLA7l4vqC2cbRbOvyDotg8PJQ7j8FDudE=\ngithub.com/libp2p/go-libp2p-record v0.2.0 h1:oiNUOCWno2BFuxt3my4i1frNrt7PerzB3queqa1NkQ0=\ngithub.com/libp2p/go-libp2p-record v0.2.0/go.mod h1:I+3zMkvvg5m2OcSdoL0KPljyJyvNDFGKX7QdlpYUcwk=\ngithub.com/libp2p/go-libp2p-routing-helpers v0.7.3 h1:u1LGzAMVRK9Nqq5aYDVOiq/HaB93U9WWczBzGyAC5ZY=\ngithub.com/libp2p/go-libp2p-routing-helpers v0.7.3/go.mod h1:cN4mJAD/7zfPKXBcs9ze31JGYAZgzdABEm+q/hkswb8=\ngithub.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA=\ngithub.com/libp2p/go-libp2p-testing v0.12.0/go.mod h1:KcGDRXyN7sQCllucn1cOOS+Dmm7ujhfEyXQL5lvkcPg=\ngithub.com/libp2p/go-libp2p-xor v0.1.0 h1:hhQwT4uGrBcuAkUGXADuPltalOdpf9aag9kaYNT2tLA=\ngithub.com/libp2p/go-libp2p-xor v0.1.0/go.mod h1:LSTM5yRnjGZbWNTA/hRwq2gGFrvRIbQJscoIL/u6InY=\ngithub.com/libp2p/go-msgio v0.0.4/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ=\ngithub.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0=\ngithub.com/libp2p/go-msgio v0.3.0/go.mod h1:nyRM819GmVaF9LX3l03RMh10QdOroF++NBbxAb0mmDM=\ngithub.com/libp2p/go-nat v0.2.0 h1:Tyz+bUFAYqGyJ/ppPPymMGbIgNRH+WqC5QrT5fKrrGk=\ngithub.com/libp2p/go-nat v0.2.0/go.mod h1:3MJr+GRpRkyT65EpVPBstXLvOlAPzUVlG6Pwg9ohLJk=\ngithub.com/libp2p/go-netroute v0.2.1 h1:V8kVrpD8GK0Riv15/7VN6RbUQ3URNZVosw7H2v9tksU=\ngithub.com/libp2p/go-netroute v0.2.1/go.mod h1:hraioZr0fhBjG0ZRXJJ6Zj2IVEVNx6tDTFQfSmcq7mQ=\ngithub.com/libp2p/go-openssl v0.0.3/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc=\ngithub.com/libp2p/go-openssl v0.0.4/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc=\ngithub.com/libp2p/go-reuseport v0.4.0 h1:nR5KU7hD0WxXCJbmw7r2rhRYruNRl2koHw8fQscQm2s=\ngithub.com/libp2p/go-reuseport v0.4.0/go.mod h1:ZtI03j/wO5hZVDFo2jKywN6bYKWLOy8Se6DrI2E1cLU=\ngithub.com/libp2p/go-yamux/v4 v4.0.1 h1:FfDR4S1wj6Bw2Pqbc8Uz7pCxeRBPbwsBbEdfwiCypkQ=\ngithub.com/libp2p/go-yamux/v4 v4.0.1/go.mod h1:NWjl8ZTLOGlozrXSOZ/HlfG++39iKNnM5wwmtQP1YB4=\ngithub.com/libp2p/zeroconf/v2 v2.2.0 h1:Cup06Jv6u81HLhIj1KasuNM/RHHrJ8T7wOTS4+Tv53Q=\ngithub.com/libp2p/zeroconf/v2 v2.2.0/go.mod h1:fuJqLnUwZTshS3U/bMRJ3+ow/v9oid1n0DmyYyNO1Xs=\ngithub.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=\ngithub.com/lyft/protoc-gen-star/v2 v2.0.3 h1:/3+/2sWyXeMLzKd1bX+ixWKgEMsULrIivpDsuaF441o=\ngithub.com/lyft/protoc-gen-star/v2 v2.0.3/go.mod h1:amey7yeodaJhXSbf/TlLvWiqQfLOSpEk//mLlc+axEk=\ngithub.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=\ngithub.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk=\ngithub.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU=\ngithub.com/maruel/circular v0.0.0-20200815005550-36e533b830e9 h1:d8OcZrg9dmqfBsHRDGP2QarJlj/1p0YI/NylTf2LYqo=\ngithub.com/maruel/circular v0.0.0-20200815005550-36e533b830e9/go.mod h1:AEsb24YMiJiSqh8Cs8kRGJxDDXKEkveJ7nxYUeYibEc=\ngithub.com/maruel/ut v1.0.2 h1:mQTlQk3jubTbdTcza+hwoZQWhzcvE4L6K6RTtAFlA1k=\ngithub.com/maruel/ut v1.0.2/go.mod h1:RV8PwPD9dd2KFlnlCc/DB2JVvkXmyaalfc5xvmSrRSs=\ngithub.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=\ngithub.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=\ngithub.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=\ngithub.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=\ngithub.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=\ngithub.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=\ngithub.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=\ngithub.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=\ngithub.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=\ngithub.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=\ngithub.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=\ngithub.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=\ngithub.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=\ngithub.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=\ngithub.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=\ngithub.com/mattn/go-runewidth v0.0.8 h1:3tS41NlGYSmhhe/8fhGRzc+z3AYCw1Fe1WAyLuujKs0=\ngithub.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=\ngithub.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=\ngithub.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=\ngithub.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=\ngithub.com/mdomke/git-semver/v5 v5.0.0 h1:By50HK/pTLR64WUUmDVtQNrVNDXsCehujhjBTAIyCHk=\ngithub.com/mdomke/git-semver/v5 v5.0.0/go.mod h1:+f5KQvxYk4WbjLPgYujVa+97Gx0dbrc4fxIK7F6fRf0=\ngithub.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=\ngithub.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=\ngithub.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=\ngithub.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=\ngithub.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=\ngithub.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=\ngithub.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs=\ngithub.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk=\ngithub.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c h1:bzE/A84HN25pxAuk9Eej1Kz9OUelF97nAc82bDquQI8=\ngithub.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c/go.mod h1:0SQS9kMwD2VsyFEB++InYyBJroV/FRmBgcydeSUcJms=\ngithub.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b h1:z78hV3sbSMAUoyUMM0I83AUIT6Hu17AWfgjzIbtrYFc=\ngithub.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b/go.mod h1:lxPUiZwKoFL8DUUmalo2yJJUCxbPKtm8OKfqr2/FTNU=\ngithub.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc h1:PTfri+PuQmWDqERdnNMiD9ZejrlswWrCpBEZgWOiTrc=\ngithub.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc/go.mod h1:cGKTAVKx4SxOuR/czcZ/E2RSJ3sfHs8FpHhQ5CWMf9s=\ngithub.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ=\ngithub.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U=\ngithub.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=\ngithub.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=\ngithub.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM=\ngithub.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=\ngithub.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=\ngithub.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ=\ngithub.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=\ngithub.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=\ngithub.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=\ngithub.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=\ngithub.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=\ngithub.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=\ngithub.com/mitchellh/reflectwalk v1.0.1 h1:FVzMWA5RllMAKIdUSC8mdWo3XtwoecrH79BY70sEEpE=\ngithub.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=\ngithub.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8=\ngithub.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=\ngithub.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=\ngithub.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=\ngithub.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=\ngithub.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA=\ngithub.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE=\ngithub.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI=\ngithub.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM=\ngithub.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0=\ngithub.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4=\ngithub.com/multiformats/go-multiaddr v0.1.0/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44=\ngithub.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo=\ngithub.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y95ncPj0vFwVF6k6wJ4=\ngithub.com/multiformats/go-multiaddr v0.12.4 h1:rrKqpY9h+n80EwhhC/kkcunCZZ7URIF8yN1WEUt2Hvc=\ngithub.com/multiformats/go-multiaddr v0.12.4/go.mod h1:sBXrNzucqkFJhvKOiwwLyqamGa/P5EIXNPLovyhQCII=\ngithub.com/multiformats/go-multiaddr-dns v0.3.0/go.mod h1:mNzQ4eTGDg0ll1N9jKPOUogZPoJ30W8a7zk66FQPpdQ=\ngithub.com/multiformats/go-multiaddr-dns v0.3.1 h1:QgQgR+LQVt3NPTjbrLLpsaT2ufAA2y0Mkk+QRVJbW3A=\ngithub.com/multiformats/go-multiaddr-dns v0.3.1/go.mod h1:G/245BRQ6FJGmryJCrOuTdB37AMA5AMOVuO6NY3JwTk=\ngithub.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E=\ngithub.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo=\ngithub.com/multiformats/go-multiaddr-net v0.1.1/go.mod h1:5JNbcfBOP4dnhoZOv10JJVkJO0pCCEf8mTnipAo2UZQ=\ngithub.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs=\ngithub.com/multiformats/go-multibase v0.0.3/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc=\ngithub.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g=\ngithub.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk=\ngithub.com/multiformats/go-multicodec v0.3.0/go.mod h1:qGGaQmioCDh+TeFOnxrbU0DaIPw8yFgAZgFG0V7p1qQ=\ngithub.com/multiformats/go-multicodec v0.9.0 h1:pb/dlPnzee/Sxv/j4PmkDRxCOi3hXTz3IbPKOXWJkmg=\ngithub.com/multiformats/go-multicodec v0.9.0/go.mod h1:L3QTQvMIaVBkXOXXtVmYE+LI16i14xuaojr/H7Ai54k=\ngithub.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U=\ngithub.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew=\ngithub.com/multiformats/go-multihash v0.0.10/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew=\ngithub.com/multiformats/go-multihash v0.0.13/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc=\ngithub.com/multiformats/go-multihash v0.0.14/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc=\ngithub.com/multiformats/go-multihash v0.0.15/go.mod h1:D6aZrWNLFTV/ynMpKsNtB40mJzmCl4jb1alC0OvHiHg=\ngithub.com/multiformats/go-multihash v0.1.0/go.mod h1:RJlXsxt6vHGaia+S8We0ErjhojtKzPP2AH4+kYM7k84=\ngithub.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U=\ngithub.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM=\ngithub.com/multiformats/go-multistream v0.5.0 h1:5htLSLl7lvJk3xx3qT/8Zm9J4K8vEOf/QGkvOGQAyiE=\ngithub.com/multiformats/go-multistream v0.5.0/go.mod h1:n6tMZiwiP2wUsR8DgfDWw1dydlEqV3l6N3/GBsX6ILA=\ngithub.com/multiformats/go-varint v0.0.1/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=\ngithub.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=\ngithub.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=\ngithub.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8=\ngithub.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU=\ngithub.com/mutecomm/go-sqlcipher/v4 v4.4.2 h1:eM10bFtI4UvibIsKr10/QT7Yfz+NADfjZYh0GKrXUNc=\ngithub.com/mutecomm/go-sqlcipher/v4 v4.4.2/go.mod h1:mF2UmIpBnzFeBdu/ypTDb/LdbS0nk0dfSN1WUsWTjMA=\ngithub.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=\ngithub.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=\ngithub.com/mwitkow/go-proto-validators v0.0.0-20180403085117-0950a7990007 h1:28i1IjGcx8AofiB4N3q5Yls55VEaitzuEPkFJEVgGkA=\ngithub.com/mwitkow/go-proto-validators v0.0.0-20180403085117-0950a7990007/go.mod h1:m2XC9Qq0AlmmVksL6FktJCdTYyLk7V3fKyp0sl1yWQo=\ngithub.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=\ngithub.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=\ngithub.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=\ngithub.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY=\ngithub.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc=\ngithub.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=\ngithub.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=\ngithub.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=\ngithub.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=\ngithub.com/onsi/ginkgo/v2 v2.17.3 h1:oJcvKpIb7/8uLpDDtnQuf18xVnwKp8DTD7DQ6gTd/MU=\ngithub.com/onsi/ginkgo/v2 v2.17.3/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc=\ngithub.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=\ngithub.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=\ngithub.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=\ngithub.com/onsi/gomega v1.33.0 h1:snPCflnZrpMsy94p4lXVEkHo12lmPnc3vY5XBbreexE=\ngithub.com/onsi/gomega v1.33.0/go.mod h1:+925n5YtiFsLzzafLUHzVMBpvvRAzrydIBiSIxjX3wY=\ngithub.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=\ngithub.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk=\ngithub.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=\ngithub.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=\ngithub.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=\ngithub.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=\ngithub.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=\ngithub.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=\ngithub.com/openzipkin/zipkin-go v0.4.3 h1:9EGwpqkgnwdEIJ+Od7QVSEIH+ocmm5nPat0G7sjsSdg=\ngithub.com/openzipkin/zipkin-go v0.4.3/go.mod h1:M9wCJZFWCo2RiY+o1eBCEMe0Dp2S5LDHcMZmk3RmK7c=\ngithub.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0=\ngithub.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y=\ngithub.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=\ngithub.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys=\ngithub.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9 h1:1/WtZae0yGtPq+TI6+Tv1WTxkukpXeMlviSxvL7SRgk=\ngithub.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9/go.mod h1:x3N5drFsm2uilKKuuYo6LdyD8vZAW55sH/9w+pbo1sw=\ngithub.com/peterbourgon/ff/v3 v3.0.0 h1:eQzEmNahuOjQXfuegsKQTSTDbf4dNvr/eNLrmJhiH7M=\ngithub.com/peterbourgon/ff/v3 v3.0.0/go.mod h1:UILIFjRH5a/ar8TjXYLTkIvSvekZqPm5Eb/qbGk6CT0=\ngithub.com/pion/datachannel v1.5.6 h1:1IxKJntfSlYkpUj8LlYRSWpYiTTC02nUrOE8T3DqGeg=\ngithub.com/pion/datachannel v1.5.6/go.mod h1:1eKT6Q85pRnr2mHiWHxJwO50SfZRtWHTsNIVb/NfGW4=\ngithub.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s=\ngithub.com/pion/dtls/v2 v2.2.11 h1:9U/dpCYl1ySttROPWJgqWKEylUdT0fXp/xst6JwY5Ks=\ngithub.com/pion/dtls/v2 v2.2.11/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE=\ngithub.com/pion/ice/v2 v2.3.24 h1:RYgzhH/u5lH0XO+ABatVKCtRd+4U1GEaCXSMjNr13tI=\ngithub.com/pion/ice/v2 v2.3.24/go.mod h1:KXJJcZK7E8WzrBEYnV4UtqEZsGeWfHxsNqhVcVvgjxw=\ngithub.com/pion/interceptor v0.1.29 h1:39fsnlP1U8gw2JzOFWdfCU82vHvhW9o0rZnZF56wF+M=\ngithub.com/pion/interceptor v0.1.29/go.mod h1:ri+LGNjRUc5xUNtDEPzfdkmSqISixVTBF/z/Zms/6T4=\ngithub.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=\ngithub.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=\ngithub.com/pion/mdns v0.0.12 h1:CiMYlY+O0azojWDmxdNr7ADGrnZ+V6Ilfner+6mSVK8=\ngithub.com/pion/mdns v0.0.12/go.mod h1:VExJjv8to/6Wqm1FXK+Ii/Z9tsVk/F5sD/N70cnYFbk=\ngithub.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=\ngithub.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=\ngithub.com/pion/rtcp v1.2.12/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9L4=\ngithub.com/pion/rtcp v1.2.14 h1:KCkGV3vJ+4DAJmvP0vaQShsb0xkRfWkO540Gy102KyE=\ngithub.com/pion/rtcp v1.2.14/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9L4=\ngithub.com/pion/rtp v1.8.3/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU=\ngithub.com/pion/rtp v1.8.6 h1:MTmn/b0aWWsAzux2AmP8WGllusBVw4NPYPVFFd7jUPw=\ngithub.com/pion/rtp v1.8.6/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU=\ngithub.com/pion/sctp v1.8.13/go.mod h1:YKSgO/bO/6aOMP9LCie1DuD7m+GamiK2yIiPM6vH+GA=\ngithub.com/pion/sctp v1.8.16 h1:PKrMs+o9EMLRvFfXq59WFsC+V8mN1wnKzqrv+3D/gYY=\ngithub.com/pion/sctp v1.8.16/go.mod h1:P6PbDVA++OJMrVNg2AL3XtYHV4uD6dvfyOovCgMs0PE=\ngithub.com/pion/sdp/v3 v3.0.9 h1:pX++dCHoHUwq43kuwf3PyJfHlwIj4hXA7Vrifiq0IJY=\ngithub.com/pion/sdp/v3 v3.0.9/go.mod h1:B5xmvENq5IXJimIO4zfp6LAe1fD9N+kFv+V/1lOdz8M=\ngithub.com/pion/srtp/v2 v2.0.18 h1:vKpAXfawO9RtTRKZJbG4y0v1b11NZxQnxRl85kGuUlo=\ngithub.com/pion/srtp/v2 v2.0.18/go.mod h1:0KJQjA99A6/a0DOVTu1PhDSw0CXF2jTkqOoMg3ODqdA=\ngithub.com/pion/stun v0.6.1 h1:8lp6YejULeHBF8NmV8e2787BogQhduZugh5PdhDyyN4=\ngithub.com/pion/stun v0.6.1/go.mod h1:/hO7APkX4hZKu/D0f2lHzNyvdkTGtIy3NDmLR7kSz/8=\ngithub.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g=\ngithub.com/pion/transport/v2 v2.2.2/go.mod h1:OJg3ojoBJopjEeECq2yJdXH9YVrUJ1uQ++NjXLOUorc=\ngithub.com/pion/transport/v2 v2.2.3/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0=\ngithub.com/pion/transport/v2 v2.2.4/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0=\ngithub.com/pion/transport/v2 v2.2.5 h1:iyi25i/21gQck4hfRhomF6SktmUQjRsRW4WJdhfc3Kc=\ngithub.com/pion/transport/v2 v2.2.5/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0=\ngithub.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0=\ngithub.com/pion/transport/v3 v3.0.2 h1:r+40RJR25S9w3jbA6/5uEPTzcdn7ncyU44RWCbHkLg4=\ngithub.com/pion/transport/v3 v3.0.2/go.mod h1:nIToODoOlb5If2jF9y2Igfx3PFYWfuXi37m0IlWa/D0=\ngithub.com/pion/turn/v2 v2.1.3/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY=\ngithub.com/pion/turn/v2 v2.1.6 h1:Xr2niVsiPTB0FPtt+yAWKFUkU1eotQbGgpTIld4x1Gc=\ngithub.com/pion/turn/v2 v2.1.6/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY=\ngithub.com/pion/webrtc/v3 v3.2.40 h1:Wtfi6AZMQg+624cvCXUuSmrKWepSB7zfgYDOYqsSOVU=\ngithub.com/pion/webrtc/v3 v3.2.40/go.mod h1:M1RAe3TNTD1tzyvqHrbVODfwdPGSXOUo/OgpoGGJqFY=\ngithub.com/piprate/json-gold v0.4.2 h1:Rq8V+637HOFcj20KdTqW/g/llCwX2qtau0g5d1pD79o=\ngithub.com/piprate/json-gold v0.4.2/go.mod h1:OK1z7UgtBZk06n2cDE2OSq1kffmjFFp5/2yhLLCz9UM=\ngithub.com/pkg/diff v0.0.0-20200914180035-5b29258ca4f7/go.mod h1:zO8QMzTeZd5cpnIkz/Gn6iK0jDfGicM1nynOkkPIl28=\ngithub.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=\ngithub.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=\ngithub.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/polydawn/refmt v0.0.0-20201211092308-30ac6d18308e/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o=\ngithub.com/polydawn/refmt v0.89.0 h1:ADJTApkvkeBZsN0tBTx8QjpD9JkmxbKp0cxfr9qszm4=\ngithub.com/polydawn/refmt v0.89.0/go.mod h1:/zvteZs/GwLtCgZ4BL6CBsk9IKIlexP43ObX9AxTqTw=\ngithub.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=\ngithub.com/pquerna/cachecontrol v0.1.0 h1:yJMy84ti9h/+OEWa752kBTKv4XC30OtVVHYv/8cTqKc=\ngithub.com/pquerna/cachecontrol v0.1.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQnrHV5K9mBcUI=\ngithub.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=\ngithub.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=\ngithub.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=\ngithub.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=\ngithub.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=\ngithub.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=\ngithub.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=\ngithub.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ=\ngithub.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=\ngithub.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=\ngithub.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=\ngithub.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=\ngithub.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=\ngithub.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=\ngithub.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=\ngithub.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=\ngithub.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=\ngithub.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=\ngithub.com/prometheus/common v0.35.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=\ngithub.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=\ngithub.com/prometheus/common v0.53.0 h1:U2pL9w9nmJwJDa4qqLQ3ZaePJ6ZTwt7cMD3AG3+aLCE=\ngithub.com/prometheus/common v0.53.0/go.mod h1:BrxBKv3FWBIGXw89Mg1AeBq7FSyRzXWI3l3e7W3RN5U=\ngithub.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=\ngithub.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=\ngithub.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=\ngithub.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=\ngithub.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=\ngithub.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=\ngithub.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=\ngithub.com/prometheus/procfs v0.15.0 h1:A82kmvXJq2jTu5YUhSGNlYoxh85zLnKgPz4bMZgI5Ek=\ngithub.com/prometheus/procfs v0.15.0/go.mod h1:Y0RJ/Y5g5wJpkTisOtqwDSo4HwhGmLB4VQSw2sQJLHk=\ngithub.com/prometheus/statsd_exporter v0.22.7 h1:7Pji/i2GuhK6Lu7DHrtTkFmNBCudCPT1pX2CziuyQR0=\ngithub.com/prometheus/statsd_exporter v0.22.7/go.mod h1:N/TevpjkIh9ccs6nuzY3jQn9dFqnUakOjnEuMPJJJnI=\ngithub.com/pseudomuto/protoc-gen-doc v1.5.1 h1:Ah259kcrio7Ix1Rhb6u8FCaOkzf9qRBqXnvAufg061w=\ngithub.com/pseudomuto/protoc-gen-doc v1.5.1/go.mod h1:XpMKYg6zkcpgfpCfQ8GcWBDRtRxOmMR5w7pz4Xo+dYM=\ngithub.com/pseudomuto/protokit v0.2.0 h1:hlnBDcy3YEDXH7kc9gV+NLaN0cDzhDvD1s7Y6FZ8RpM=\ngithub.com/pseudomuto/protokit v0.2.0/go.mod h1:2PdH30hxVHsup8KpBTOXTBeMVhJZVio3Q8ViKSAXT0Q=\ngithub.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=\ngithub.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=\ngithub.com/quic-go/quic-go v0.44.0 h1:So5wOr7jyO4vzL2sd8/pD9Kesciv91zSk8BoFngItQ0=\ngithub.com/quic-go/quic-go v0.44.0/go.mod h1:z4cx/9Ny9UtGITIPzmPTXh1ULfOyWh4qGQlpnPcWmek=\ngithub.com/quic-go/webtransport-go v0.8.0 h1:HxSrwun11U+LlmwpgM1kEqIqH90IT4N8auv/cD7QFJg=\ngithub.com/quic-go/webtransport-go v0.8.0/go.mod h1:N99tjprW432Ut5ONql/aUhSLT0YVSlwHohQsuac9WaM=\ngithub.com/raulk/go-watchdog v1.3.0 h1:oUmdlHxdkXRJlwfG0O9omj8ukerm8MEQavSiDTEtBsk=\ngithub.com/raulk/go-watchdog v1.3.0/go.mod h1:fIvOnLbF0b0ZwkB9YU4mOW9Did//4vPZtDqv66NfsMU=\ngithub.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=\ngithub.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=\ngithub.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=\ngithub.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=\ngithub.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo=\ngithub.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=\ngithub.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=\ngithub.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk=\ngithub.com/samber/lo v1.39.0 h1:4gTz1wUhNYLhFSKl6O+8peW0v2F4BCY034GRpU9WnuA=\ngithub.com/samber/lo v1.39.0/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=\ngithub.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=\ngithub.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=\ngithub.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=\ngithub.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0=\ngithub.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=\ngithub.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=\ngithub.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw=\ngithub.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI=\ngithub.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU=\ngithub.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag=\ngithub.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg=\ngithub.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw=\ngithub.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y=\ngithub.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=\ngithub.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q=\ngithub.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ=\ngithub.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I=\ngithub.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0=\ngithub.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ=\ngithub.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk=\ngithub.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=\ngithub.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=\ngithub.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=\ngithub.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=\ngithub.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=\ngithub.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=\ngithub.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=\ngithub.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=\ngithub.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs=\ngithub.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=\ngithub.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=\ngithub.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg=\ngithub.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM=\ngithub.com/smola/gocompat v0.2.0/go.mod h1:1B0MlxbmoZNo3h8guHp8HztB3BSYR5itql9qtVc0ypY=\ngithub.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=\ngithub.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=\ngithub.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc=\ngithub.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=\ngithub.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=\ngithub.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=\ngithub.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=\ngithub.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=\ngithub.com/spf13/afero v1.5.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=\ngithub.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY=\ngithub.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=\ngithub.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=\ngithub.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=\ngithub.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=\ngithub.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=\ngithub.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=\ngithub.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=\ngithub.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=\ngithub.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=\ngithub.com/square/go-jose/v3 v3.0.0-20200630053402-0a67ce9b0693 h1:wD1IWQwAhdWclCwaf6DdzgCAe9Bfz1M+4AHRd7N786Y=\ngithub.com/square/go-jose/v3 v3.0.0-20200630053402-0a67ce9b0693/go.mod h1:6hSY48PjDm4UObWmGLyJE9DxYVKTgR9kbCspXXJEhcU=\ngithub.com/src-d/envconfig v1.0.0/go.mod h1:Q9YQZ7BKITldTBnoxsE5gOeB5y66RyPXeue/R4aaNBc=\ngithub.com/srikrsna/protoc-gen-gotag v1.0.1 h1:zMDkplPjcpIOafgfXTD1BqCrMGycXcomV794MIKKi9s=\ngithub.com/srikrsna/protoc-gen-gotag v1.0.1/go.mod h1:HiXK5kcp/ZRnNPahuJm3tzfGDoD8xzvLNdg5/PYKq7Q=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=\ngithub.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=\ngithub.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=\ngithub.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=\ngithub.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=\ngithub.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=\ngithub.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=\ngithub.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=\ngithub.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=\ngithub.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=\ngithub.com/stvp/go-udp-testing v0.0.0-20201019212854-469649b16807/go.mod h1:7jxmlfBCDBXRzr0eAQJ48XC1hBu1np4CS5+cHEYfwpc=\ngithub.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=\ngithub.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY=\ngithub.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=\ngithub.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502/go.mod h1:p9lPsd+cx33L3H9nNoecRRxPssFKUwwI50I3pZ0yT+8=\ngithub.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=\ngithub.com/teserakt-io/golang-ed25519 v0.0.0-20210104091850-3888c087a4c8 h1:RBkacARv7qY5laaXGlF4wFB/tk5rnthhPb8oIBGoagY=\ngithub.com/teserakt-io/golang-ed25519 v0.0.0-20210104091850-3888c087a4c8/go.mod h1:9PdLyPiZIiW3UopXyRnPYyjUXSpiQNHRLu8fOsR3o8M=\ngithub.com/tj/assert v0.0.3 h1:Df/BlaZ20mq6kuai7f5z2TvPFiwC3xaWJSDQNiIS3Rk=\ngithub.com/tj/assert v0.0.3/go.mod h1:Ne6X72Q+TB1AteidzQncjw9PabbMp4PBMZ1k+vd1Pvk=\ngithub.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c h1:u6SKchux2yDvFQnDHS3lPnIRmfVJ5Sxy3ao2SIdysLQ=\ngithub.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM=\ngithub.com/ucarion/urlpath v0.0.0-20200424170820-7ccc79b76bbb h1:Ywfo8sUltxogBpFuMOFRrrSifO788kAFxmvVw31PtQQ=\ngithub.com/ucarion/urlpath v0.0.0-20200424170820-7ccc79b76bbb/go.mod h1:ikPs9bRWicNw3S7XpJ8sK/smGwU9WcSVU3dy9qahYBM=\ngithub.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=\ngithub.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=\ngithub.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=\ngithub.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=\ngithub.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=\ngithub.com/wangjia184/sortedset v0.0.0-20160527075905-f5d03557ba30/go.mod h1:YkocrP2K2tcw938x9gCOmT5G5eCD6jsTz0SZuyAqwIE=\ngithub.com/warpfork/go-testmark v0.3.0/go.mod h1:jhEf8FVxd+F17juRubpmut64NEG6I2rgkUhlcqqXwE0=\ngithub.com/warpfork/go-testmark v0.9.0/go.mod h1:jhEf8FVxd+F17juRubpmut64NEG6I2rgkUhlcqqXwE0=\ngithub.com/warpfork/go-testmark v0.12.1 h1:rMgCpJfwy1sJ50x0M0NgyphxYYPMOODIJHhsXyEHU0s=\ngithub.com/warpfork/go-testmark v0.12.1/go.mod h1:kHwy7wfvGSPh1rQJYKayD4AbtNaeyZdcGi9tNJTaa5Y=\ngithub.com/warpfork/go-wish v0.0.0-20200122115046-b9ea61034e4a/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw=\ngithub.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0 h1:GDDkbFiaK8jsSDJfjId/PEGEShv6ugrt4kYsC5UIDaQ=\ngithub.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw=\ngithub.com/whyrusleeping/base32 v0.0.0-20170828182744-c30ac30633cc h1:BCPnHtcboadS0DvysUuJXZ4lWVv5Bh5i7+tbIyi+ck4=\ngithub.com/whyrusleeping/base32 v0.0.0-20170828182744-c30ac30633cc/go.mod h1:r45hJU7yEoA81k6MWNhpMj/kms0n14dkzkxYHoB96UM=\ngithub.com/whyrusleeping/cbor v0.0.0-20171005072247-63513f603b11 h1:5HZfQkwe0mIfyDmc1Em5GqlNRzcdtlv4HTNmdpt7XH0=\ngithub.com/whyrusleeping/cbor v0.0.0-20171005072247-63513f603b11/go.mod h1:Wlo/SzPmxVp6vXpGt/zaXhHH0fn4IxgqZc82aKg6bpQ=\ngithub.com/whyrusleeping/cbor-gen v0.1.1 h1:eKfcJIoxivjMtwfCfmJAqSF56MHcWqyIScXwaC1VBgw=\ngithub.com/whyrusleeping/cbor-gen v0.1.1/go.mod h1:pM99HXyEbSQHcosHc0iW7YFmwnscr+t9Te4ibko05so=\ngithub.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f h1:jQa4QT2UP9WYv2nzyawpKMOCl+Z/jW7djv2/J50lj9E=\ngithub.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f/go.mod h1:p9UJB6dDgdPgMJZs7UjUOdulKyRr9fqkS+6JKAInPy8=\ngithub.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 h1:EKhdznlJHPMoKr0XTrX+IlJs1LH3lyx2nfr1dOlZ79k=\ngithub.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1/go.mod h1:8UvriyWtv5Q5EOgjHaSseUEdkQfvwFv1I/In/O2M9gc=\ngithub.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc/go.mod h1:bopw91TMyo8J3tvftk8xmU2kPmlrt4nScJQZU2hE5EM=\ngithub.com/whyrusleeping/go-sysinfo v0.0.0-20190219211824-4a357d4b90b1 h1:ctS9Anw/KozviCCtK6VWMz5kPL9nbQzbQY4yfqlIV4M=\ngithub.com/whyrusleeping/go-sysinfo v0.0.0-20190219211824-4a357d4b90b1/go.mod h1:tKH72zYNt/exx6/5IQO6L9LoQ0rEjd5SbbWaDTs9Zso=\ngithub.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 h1:E9S12nwJwEOXe2d6gT6qxdvqMnNq+VnSsKPgm2ZZNds=\ngithub.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7/go.mod h1:X2c0RVCI1eSUFI8eLcY3c0423ykwiUdxLJtkDvruhjI=\ngithub.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=\ngithub.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=\ngithub.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=\ngithub.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=\ngithub.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngithub.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=\ngo.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=\ngo.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=\ngo.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=\ngo.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA=\ngo.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=\ngo.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=\ngo.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=\ngo.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0 h1:Xs2Ncz0gNihqu9iosIZ5SkBbWo5T8JhhLJFMQL1qmLI=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0/go.mod h1:vy+2G/6NvVMpwGX/NyLqcC41fxepnuKHk16E6IZUcJc=\ngo.opentelemetry.io/otel v1.26.0 h1:LQwgL5s/1W7YiiRwxf03QGnWLb2HW4pLiAhaA5cZXBs=\ngo.opentelemetry.io/otel v1.26.0/go.mod h1:UmLkJHUAidDval2EICqBMbnAd0/m2vmpf/dAM+fvFs4=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.26.0 h1:1u/AyyOqAWzy+SkPxDpahCNZParHV8Vid1RnI2clyDE=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.26.0/go.mod h1:z46paqbJ9l7c9fIPCXTqTGwhQZ5XoTIsfeFYWboizjs=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.26.0 h1:Waw9Wfpo/IXzOI8bCB7DIk+0JZcqqsyn1JFnAc+iam8=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.26.0/go.mod h1:wnJIG4fOqyynOnnQF/eQb4/16VlX2EJAHhHgqIqWfAo=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.26.0 h1:1wp/gyxsuYtuE/JFxsQRtcCDtMrO2qMvlfXALU5wkzI=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.26.0/go.mod h1:gbTHmghkGgqxMomVQQMur1Nba4M0MQ8AYThXDUjsJ38=\ngo.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.26.0 h1:0W5o9SzoR15ocYHEQfvfipzcNog1lBxOLfnex91Hk6s=\ngo.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.26.0/go.mod h1:zVZ8nz+VSggWmnh6tTsJqXQ7rU4xLwRtna1M4x5jq58=\ngo.opentelemetry.io/otel/exporters/zipkin v1.26.0 h1:sBk6A62GgcQRwcxcBwRMPkqeuSizcpHkXyZNyP281Fw=\ngo.opentelemetry.io/otel/exporters/zipkin v1.26.0/go.mod h1:fLzYtPUxPFzu7rSqhYsCxYheT2dNoPjtKovCLzLm07w=\ngo.opentelemetry.io/otel/metric v1.26.0 h1:7S39CLuY5Jgg9CrnA9HHiEjGMF/X2VHvoXGgSllRz30=\ngo.opentelemetry.io/otel/metric v1.26.0/go.mod h1:SY+rHOI4cEawI9a7N1A4nIg/nTQXe1ccCNWYOJUrpX4=\ngo.opentelemetry.io/otel/sdk v1.26.0 h1:Y7bumHf5tAiDlRYFmGqetNcLaVUZmh4iYfmGxtmz7F8=\ngo.opentelemetry.io/otel/sdk v1.26.0/go.mod h1:0p8MXpqLeJ0pzcszQQN4F0S5FVjBLgypeGSngLsmirs=\ngo.opentelemetry.io/otel/trace v1.26.0 h1:1ieeAUb4y0TE26jUFrCIXKpTuVK7uJGN9/Z/2LP5sQA=\ngo.opentelemetry.io/otel/trace v1.26.0/go.mod h1:4iDxvGDQuUkHve82hJJ8UqrwswHYsZuWCBllGV2U2y0=\ngo.opentelemetry.io/proto/otlp v1.2.0 h1:pVeZGk7nXDC9O2hncA6nHldxEjm6LByfA2aN8IOkz94=\ngo.opentelemetry.io/proto/otlp v1.2.0/go.mod h1:gGpR8txAl5M03pDhMC79G6SdqNV26naRm/KDsgaHD8A=\ngo.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=\ngo.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=\ngo.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=\ngo.uber.org/atomic v1.8.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=\ngo.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=\ngo.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=\ngo.uber.org/dig v1.17.1 h1:Tga8Lz8PcYNsWsyHMZ1Vm0OQOUaJNDyvPImgbAu9YSc=\ngo.uber.org/dig v1.17.1/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE=\ngo.uber.org/fx v1.21.1 h1:RqBh3cYdzZS0uqwVeEjOX2p73dddLpym315myy/Bpb0=\ngo.uber.org/fx v1.21.1/go.mod h1:HT2M7d7RHo+ebKGh9NRcrsrHHfpZ60nW3QRubMRfv48=\ngo.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=\ngo.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=\ngo.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=\ngo.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=\ngo.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=\ngo.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=\ngo.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=\ngo.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=\ngo.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=\ngo.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=\ngo.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=\ngo.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=\ngo.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=\ngo.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=\ngo.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=\ngo.uber.org/zap v1.14.1/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=\ngo.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=\ngo.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=\ngo.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=\ngo.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=\ngo.uber.org/zap v1.20.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=\ngo.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=\ngo.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=\ngo4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=\ngo4.org v0.0.0-20200411211856-f5505b9728dd/go.mod h1:CIiUVy99QCPfoE13bO4EZaz5GZMZXMSBGhxRdsvzbkg=\ngo4.org v0.0.0-20230225012048-214862532bf5 h1:nifaUDeh+rPaBCMPMQHZmvJf+QdpLFnuQPwx+LxVmtc=\ngo4.org v0.0.0-20230225012048-214862532bf5/go.mod h1:F57wTi5Lrj6WLyswp5EYV1ncrEbFGHD4hhz6S1ZYeaU=\ngolang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=\ngolang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=\ngolang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=\ngolang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=\ngolang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=\ngolang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=\ngolang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=\ngolang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=\ngolang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=\ngolang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=\ngolang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=\ngolang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=\ngolang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=\ngolang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=\ngolang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=\ngolang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=\ngolang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=\ngolang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=\ngolang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=\ngolang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=\ngolang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=\ngolang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=\ngolang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=\ngolang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=\ngolang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=\ngolang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=\ngolang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=\ngolang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=\ngolang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=\ngolang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=\ngolang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=\ngolang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190227160552-c95aed5357e7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=\ngolang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=\ngolang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=\ngolang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=\ngolang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=\ngolang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=\ngolang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=\ngolang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=\ngolang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=\ngolang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=\ngolang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=\ngolang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=\ngolang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=\ngolang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=\ngolang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=\ngolang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=\ngolang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=\ngolang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo=\ngolang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=\ngolang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=\ngolang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=\ngolang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210426080607-c94f62235c83/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=\ngolang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\ngolang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=\ngolang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=\ngolang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=\ngolang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=\ngolang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=\ngolang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=\ngolang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=\ngolang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=\ngolang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=\ngolang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=\ngolang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=\ngolang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=\ngolang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=\ngolang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=\ngolang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=\ngolang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=\ngolang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=\ngolang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=\ngolang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20181130052023-1c3d964395ce/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=\ngolang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=\ngolang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=\ngolang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200806022845-90696ccdc692/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=\ngolang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20201211185031-d93e913c1a58/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=\ngolang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\ngolang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=\ngolang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=\ngolang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=\ngolang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=\ngolang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=\ngonum.org/v1/gonum v0.15.0 h1:2lYxjRbTYyxkJxlhC+LvJIx3SsANPdRybu1tGj9/OrQ=\ngonum.org/v1/gonum v0.15.0/go.mod h1:xzZVBJBtS+Mz4q0Yl2LJTk+OxOg4jiXZ7qBoM0uISGo=\ngoogle.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=\ngoogle.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=\ngoogle.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=\ngoogle.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=\ngoogle.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=\ngoogle.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=\ngoogle.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=\ngoogle.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=\ngoogle.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=\ngoogle.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=\ngoogle.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=\ngoogle.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=\ngoogle.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=\ngoogle.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=\ngoogle.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=\ngoogle.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=\ngoogle.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=\ngoogle.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=\ngoogle.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd h1:OjndDrsik+Gt+e6fs45z9AxiewiKyLKYpA45W5Kpkks=\ngoogle.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE=\ngoogle.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=\ngoogle.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=\ngoogle.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=\ngoogle.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=\ngoogle.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=\ngoogle.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=\ngoogle.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=\ngoogle.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=\ngoogle.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=\ngoogle.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=\ngoogle.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=\ngoogle.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=\ngoogle.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=\ngoogle.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 h1:F29+wU6Ee6qgu9TddPgooOdaqsxTMunOoj8KA5yuS5A=\ngoogle.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1/go.mod h1:5KF+wpkbTSbGcR9zteSqZV6fqFOWBl4Yde8En8MryZA=\ngoogle.golang.org/grpc/examples v0.0.0-20200922230038-4e932bbcb079 h1:unzgkDPNegIn/czOcgxzQaTzEzOiBH1V1j55rsEzVEg=\ngoogle.golang.org/grpc/examples v0.0.0-20200922230038-4e932bbcb079/go.mod h1:Lh55/1hxmVHEkOvSIQ2uj0P12QyOCUNyRwnUlSS13hw=\ngoogle.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=\ngoogle.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=\ngoogle.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=\ngoogle.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=\ngoogle.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=\ngoogle.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=\ngoogle.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=\ngoogle.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=\ngoogle.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=\ngoogle.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=\ngoogle.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=\ngoogle.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=\ngoogle.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=\ngoogle.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=\ngopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=\ngopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=\ngopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=\ngopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=\ngopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=\ngopkg.in/src-d/go-cli.v0 v0.0.0-20181105080154-d492247bbc0d/go.mod h1:z+K8VcOYVYcSwSjGebuDL6176A1XskgbtNl64NSg+n8=\ngopkg.in/src-d/go-log.v1 v1.0.1/go.mod h1:GN34hKP0g305ysm2/hctJ0Y8nWP3zxXXJ8GFabTyABE=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=\ngopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngrpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=\nhonnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=\nhonnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=\nhonnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=\nlukechampine.com/blake3 v1.1.6/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA=\nlukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE=\nlukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=\nmoul.io/banner v1.0.1 h1:+WsemGLhj2pOajw2eR5VYjLhOIqs0XhIRYchzTyMLk0=\nmoul.io/banner v1.0.1/go.mod h1:XwvIGKkhKRKyN1vIdmR5oaKQLIkMhkMqrsHpS94QzAU=\nmoul.io/godev v1.7.0/go.mod h1:5lgSpI1oH7xWpLl2Ew/Nsgk8DiNM6FzN9WV9+lgW8RQ=\nmoul.io/motd v1.0.0 h1:Trk4fPibDfPJf2iCBSQC8ws7Q02sMwivQdVEFAjCPto=\nmoul.io/motd v1.0.0/go.mod h1:39rvZ0lC2oRhHDY2VoPyZ8r70VKqeJye3QAxjeLDJso=\nmoul.io/openfiles v1.2.0 h1:oAmBX0ChBBsKERFfTRyLi9JkjOvEv9E474BEL/wVq44=\nmoul.io/openfiles v1.2.0/go.mod h1:FR9BZ1mw7VE0uZN6HVJcA16Ee2nTDG/YZUyiGM/T2Rw=\nmoul.io/srand v1.6.1 h1:SJ335F+54ivLdlH7wH52Rtyv0Ffos6DpsF5wu3ZVMXU=\nmoul.io/srand v1.6.1/go.mod h1:P2uaZB+GFstFNo8sEj6/U8FRV1n25kD0LLckFpJ+qvc=\nmoul.io/testman v1.5.0 h1:tN1XEzLxYh8ZYy1wET6leWufnTl7BcZELkSNiro/yEo=\nmoul.io/testman v1.5.0/go.mod h1:b4/5+lMsMDJtwuh25Cr0eVJ5Y4B2lSPfkzDtfct070g=\nmoul.io/u v1.6.0/go.mod h1:yd3/IoYRIJaZWAJV2rYHvM2EPp/Pp0zSNraB5IPX+hw=\nmoul.io/u v1.27.0 h1:rF0p184mludn2DzL0unA8Gf/mFWMBerdqOh8cyuQYzQ=\nmoul.io/u v1.27.0/go.mod h1:ggYDXxUjoHpfDsMPD3STqkUZTyA741PZiQhSd+7kRnA=\nmoul.io/zapfilter v1.7.0 h1:7aFrG4N72bDH9a2BtYUuUaDS981Dxu3qybWfeqaeBDU=\nmoul.io/zapfilter v1.7.0/go.mod h1:M+N2s+qZiA+bzRoyKMVRxyuERijS2ovi2pnMyiOGMvc=\nmoul.io/zapring v1.3.3 h1:N2QPn6qTMBWjh842UPxdjj2UW+uH/foXohgGCPZDlM8=\nmoul.io/zapring v1.3.3/go.mod h1:UvlTrdjeHtSqdjkGXwAxIfpaQ/ai4I+ccRASFxflcJE=\nmvdan.cc/gofumpt v0.4.0 h1:JVf4NN1mIpHogBj7ABpgOyZc65/UUOkKQFkoURsz4MM=\nmvdan.cc/gofumpt v0.4.0/go.mod h1:PljLOHDeZqgS8opHRKLzp2It2VBuSdteAgqUfzMTxlQ=\npgregory.net/rapid v0.4.7 h1:MTNRktPuv5FNqOO151TM9mDTa+XHcX6ypYeISDVD14g=\npgregory.net/rapid v0.4.7/go.mod h1:UYpPVyjFHzYBGHIxLFoupi8vwk6rXNzRY9OMvVxFIOU=\nrsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=\nrsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=\nrsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=\nsourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=\nsourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=\n"
  },
  {
    "path": "group.go",
    "content": "package weshnet\n\nimport (\n\t\"github.com/libp2p/go-libp2p/core/crypto\"\n\t\"google.golang.org/protobuf/proto\"\n\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n)\n\nconst CurrentGroupVersion = 1\n\n// NewGroupMultiMember creates a new Group object and an invitation to be used by\n// the first member of the group\nfunc NewGroupMultiMember() (*protocoltypes.Group, crypto.PrivKey, error) {\n\treturn protocoltypes.NewGroupMultiMember()\n}\n\nfunc getAndFilterGroupDeviceChainKeyAddedPayload(m *protocoltypes.GroupMetadata, localMemberPublicKey crypto.PubKey) (crypto.PubKey, []byte, error) {\n\tif m == nil || m.EventType != protocoltypes.EventType_EventTypeGroupDeviceChainKeyAdded {\n\t\treturn nil, nil, errcode.ErrCode_ErrInvalidInput\n\t}\n\n\ts := &protocoltypes.GroupDeviceChainKeyAdded{}\n\tif err := proto.Unmarshal(m.Payload, s); err != nil {\n\t\treturn nil, nil, errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\tsenderDevicePubKey, err := crypto.UnmarshalEd25519PublicKey(s.DevicePk)\n\tif err != nil {\n\t\treturn nil, nil, errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\tdestMemberPubKey, err := crypto.UnmarshalEd25519PublicKey(s.DestMemberPk)\n\tif err != nil {\n\t\treturn nil, nil, errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\tif !localMemberPublicKey.Equals(destMemberPubKey) {\n\t\treturn nil, nil, errcode.ErrCode_ErrGroupSecretOtherDestMember\n\t}\n\n\treturn senderDevicePubKey, s.Payload, nil\n}\n"
  },
  {
    "path": "group_context.go",
    "content": "package weshnet\n\nimport (\n\t\"context\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/libp2p/go-libp2p/core/crypto\"\n\t\"go.uber.org/zap\"\n\t\"google.golang.org/protobuf/proto\"\n\n\t\"berty.tech/go-orbit-db/stores\"\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n\t\"berty.tech/weshnet/v2/pkg/ipfsutil\"\n\t\"berty.tech/weshnet/v2/pkg/logutil\"\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n\t\"berty.tech/weshnet/v2/pkg/secretstore\"\n)\n\ntype GroupContext struct {\n\tctx             context.Context\n\tcancel          context.CancelFunc\n\tgroup           *protocoltypes.Group\n\tmetadataStore   *MetadataStore\n\tmessageStore    *MessageStore\n\tsecretStore     secretstore.SecretStore\n\townMemberDevice secretstore.OwnMemberDevice\n\n\tlogger            *zap.Logger\n\tclosed            uint32\n\ttasks             sync.WaitGroup\n\tdevicesAdded      map[string]chan struct{}\n\tmuDevicesAdded    sync.RWMutex\n\tselfAnnounced     chan struct{}\n\tselfAnnouncedOnce sync.Once\n}\n\nfunc (gc *GroupContext) SecretStore() secretstore.SecretStore {\n\treturn gc.secretStore\n}\n\nfunc (gc *GroupContext) MessageStore() *MessageStore {\n\treturn gc.messageStore\n}\n\nfunc (gc *GroupContext) MetadataStore() *MetadataStore {\n\treturn gc.metadataStore\n}\n\nfunc (gc *GroupContext) Group() *protocoltypes.Group {\n\treturn gc.group\n}\n\nfunc (gc *GroupContext) MemberPubKey() crypto.PubKey {\n\treturn gc.ownMemberDevice.Member()\n}\n\nfunc (gc *GroupContext) DevicePubKey() crypto.PubKey {\n\treturn gc.ownMemberDevice.Device()\n}\n\nfunc (gc *GroupContext) Close() error {\n\tgc.cancel()\n\n\t// @NOTE(gfanton): wait for active tasks to end, doing this we avoid to do\n\t// some operations on a closed store\n\tgc.tasks.Wait()\n\n\t// mark group context has closed\n\tatomic.StoreUint32(&gc.closed, 1)\n\n\t// @FIXME(gfanton): should we really handle store closing here ?\n\tgc.metadataStore.Close()\n\tgc.messageStore.Close()\n\n\tgc.logger.Debug(\"group context closed\", zap.String(\"groupID\", gc.group.GroupIDAsString()))\n\treturn nil\n}\n\nfunc (gc *GroupContext) IsClosed() bool {\n\treturn atomic.LoadUint32(&gc.closed) != 0\n}\n\nfunc NewContextGroup(group *protocoltypes.Group, metadataStore *MetadataStore, messageStore *MessageStore, secretStore secretstore.SecretStore, memberDevice secretstore.OwnMemberDevice, logger *zap.Logger) *GroupContext {\n\tctx, cancel := context.WithCancel(context.Background())\n\n\tif logger == nil {\n\t\tlogger = zap.NewNop()\n\t}\n\n\treturn &GroupContext{\n\t\tctx:             ctx,\n\t\tcancel:          cancel,\n\t\tgroup:           group,\n\t\tmetadataStore:   metadataStore,\n\t\tmessageStore:    messageStore,\n\t\tsecretStore:     secretStore,\n\t\townMemberDevice: memberDevice,\n\t\tlogger:          logger.With(logutil.PrivateString(\"group-id\", fmt.Sprintf(\"%.6s\", base64.StdEncoding.EncodeToString(group.PublicKey)))),\n\t\tclosed:          0,\n\t\tdevicesAdded:    make(map[string]chan struct{}),\n\t\tselfAnnounced:   make(chan struct{}),\n\t}\n}\n\nfunc (gc *GroupContext) ActivateGroupContext(contactPK crypto.PubKey) (err error) {\n\tctx := gc.ctx\n\n\t// start watching for GroupMetadataEvent to send secret and register\n\t// chainkey of new members.\n\t{\n\t\tm := gc.MetadataStore()\n\t\tsub, err := m.EventBus().Subscribe(new(*protocoltypes.GroupMetadataEvent))\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"unable to subscribe to group metadata event: %w\", err)\n\t\t}\n\n\t\tgc.tasks.Add(1)\n\t\tgo func() {\n\t\t\tdefer gc.tasks.Done() // ultimately, mark bg task has done\n\t\t\tdefer sub.Close()\n\n\t\t\tfor {\n\t\t\t\tvar evt any\n\t\t\t\tselect {\n\t\t\t\tcase <-ctx.Done():\n\t\t\t\t\treturn\n\t\t\t\tcase evt = <-sub.Out():\n\t\t\t\t}\n\n\t\t\t\t// @TODO(gfanton): should we handle this in a sub gorouting ?\n\t\t\t\te := evt.(*protocoltypes.GroupMetadataEvent)\n\t\t\t\t// start := time.Now()\n\t\t\t\tif err := gc.handleGroupMetadataEvent(e); err != nil {\n\t\t\t\t\tgc.logger.Error(\"unable to handle EventTypeGroupDeviceSecretAdded\", zap.Error(err))\n\t\t\t\t}\n\n\t\t\t\t// if t := time.Since(start).Milliseconds(); t > 0 {\n\t\t\t\t// \tfmt.Printf(\"elapsed: %dms\\n\", t)\n\t\t\t\t// }\n\t\t\t}\n\t\t}()\n\t}\n\n\t// send secret and register key from existing members.\n\t// we should wait until all the events have been retrieved.\n\t{\n\t\tvar wgExistingMembers sync.WaitGroup\n\n\t\twgExistingMembers.Add(2)\n\n\t\tgo func() {\n\t\t\tstart := time.Now()\n\t\t\tgc.fillMessageKeysHolderUsingPreviousData()\n\t\t\twgExistingMembers.Done()\n\t\t\tgc.logger.Info(fmt.Sprintf(\"FillMessageKeysHolderUsingPreviousData took %s\", time.Since(start)))\n\t\t}()\n\n\t\tgo func() {\n\t\t\tstart := time.Now()\n\t\t\tgc.sendSecretsToExistingMembers(contactPK)\n\t\t\twgExistingMembers.Done()\n\t\t\tgc.logger.Info(fmt.Sprintf(\"SendSecretsToExistingMembers took %s\", time.Since(start)))\n\t\t}()\n\n\t\twgExistingMembers.Wait()\n\t}\n\n\tstart := time.Now()\n\top, err := gc.MetadataStore().AddDeviceToGroup(gc.ctx)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to add device to groupo: %w\", err)\n\t}\n\n\tgc.logger.Info(fmt.Sprintf(\"AddDeviceToGroup took %s\", time.Since(start)))\n\tif op != nil {\n\t\t// Waiting for async events to be handled\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\treturn ctx.Err()\n\t\tcase <-gc.selfAnnounced: // device has been selfAnnounced\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (gc *GroupContext) handleGroupMetadataEvent(e *protocoltypes.GroupMetadataEvent) (err error) {\n\tswitch e.Metadata.EventType {\n\tcase protocoltypes.EventType_EventTypeGroupMemberDeviceAdded:\n\t\tevent := &protocoltypes.GroupMemberDeviceAdded{}\n\t\tif err := proto.Unmarshal(e.Event, event); err != nil {\n\t\t\tgc.logger.Error(\"unable to unmarshal payload\", zap.Error(err))\n\t\t}\n\n\t\tmemberPK, err := crypto.UnmarshalEd25519PublicKey(event.MemberPk)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"unable to unmarshal sender member pk: %w\", err)\n\t\t}\n\n\t\tif memberPK.Equals(gc.ownMemberDevice.Member()) {\n\t\t\tgc.selfAnnouncedOnce.Do(func() { close(gc.selfAnnounced) }) // mark has self announced\n\t\t}\n\n\t\tif _, err := gc.MetadataStore().SendSecret(gc.ctx, memberPK); err != nil {\n\t\t\tif !errcode.Is(err, errcode.ErrCode_ErrGroupSecretAlreadySentToMember) {\n\t\t\t\treturn fmt.Errorf(\"unable to send secret to member: %w\", err)\n\t\t\t}\n\t\t}\n\n\tcase protocoltypes.EventType_EventTypeGroupDeviceChainKeyAdded:\n\t\tsenderPublicKey, encryptedDeviceChainKey, err := getAndFilterGroupDeviceChainKeyAddedPayload(e.Metadata, gc.ownMemberDevice.Member())\n\t\tswitch err {\n\t\tcase nil: // ok\n\t\tcase errcode.ErrCode_ErrInvalidInput, errcode.ErrCode_ErrGroupSecretOtherDestMember:\n\t\t\t// @FIXME(gfanton): should we log this ?\n\t\t\treturn nil\n\t\tdefault:\n\t\t\treturn fmt.Errorf(\"an error occurred while opening device secrets: %w\", err)\n\t\t}\n\n\t\tif err = gc.SecretStore().RegisterChainKey(gc.ctx, gc.Group(), senderPublicKey, encryptedDeviceChainKey); err != nil {\n\t\t\treturn fmt.Errorf(\"unable to register chain key: %w\", err)\n\t\t}\n\n\t\tif rawPK, err := senderPublicKey.Raw(); err == nil {\n\t\t\t// A new chainKey has been registered, notify watcher\n\t\t\tgo gc.notifyDeviceAdded(rawPK)\n\t\t\t// process queued message and check if cached messages can be opened with it\n\t\t\tgc.MessageStore().ProcessMessageQueueForDevicePK(gc.ctx, rawPK)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (gc *GroupContext) fillMessageKeysHolderUsingPreviousData() {\n\tpublishedSecrets := gc.metadataStoreListSecrets()\n\n\tfor senderPublicKey, encryptedSecret := range publishedSecrets {\n\t\tif err := gc.SecretStore().RegisterChainKey(gc.ctx, gc.Group(), senderPublicKey, encryptedSecret); err != nil {\n\t\t\tgc.logger.Error(\"unable to register chain key\", zap.Error(err))\n\t\t\tcontinue\n\t\t}\n\t\t// A new chainKey is registered, check if cached messages can be opened with it\n\t\tif rawPK, err := senderPublicKey.Raw(); err == nil {\n\t\t\tgc.MessageStore().ProcessMessageQueueForDevicePK(gc.ctx, rawPK)\n\t\t}\n\t}\n}\n\nfunc (gc *GroupContext) metadataStoreListSecrets() map[crypto.PubKey][]byte {\n\tpublishedSecrets := map[crypto.PubKey][]byte{}\n\n\tm := gc.MetadataStore()\n\n\tmetadatas, err := m.ListEvents(gc.ctx, nil, nil, false)\n\tif err != nil {\n\t\treturn nil\n\t}\n\tfor metadata := range metadatas {\n\t\tif metadata == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tpk, encryptedDeviceChainKey, err := getAndFilterGroupDeviceChainKeyAddedPayload(metadata.Metadata, gc.MemberPubKey())\n\t\tif errcode.Is(err, errcode.ErrCode_ErrInvalidInput) || errcode.Is(err, errcode.ErrCode_ErrGroupSecretOtherDestMember) {\n\t\t\tcontinue\n\t\t}\n\n\t\tif err != nil {\n\t\t\tgc.logger.Error(\"unable to open chain key\", zap.Error(err))\n\t\t\tcontinue\n\t\t}\n\n\t\tpublishedSecrets[pk] = encryptedDeviceChainKey\n\t}\n\n\treturn publishedSecrets\n}\n\nfunc (gc *GroupContext) sendSecretsToExistingMembers(contact crypto.PubKey) {\n\tmembers := gc.MetadataStore().ListMembers()\n\n\t// Force sending secret to contact member in contact group\n\tif gc.group.GroupType == protocoltypes.GroupType_GroupTypeContact && len(members) < 2 && contact != nil {\n\t\t// Check if contact member is already listed\n\t\tfound := false\n\t\tfor _, member := range members {\n\t\t\tif member.Equals(contact) {\n\t\t\t\tfound = true\n\t\t\t}\n\t\t}\n\n\t\t// If not listed, add it to the list\n\t\tif !found {\n\t\t\tmembers = append(members, contact)\n\t\t}\n\t}\n\n\tfor _, pk := range members {\n\t\trawPK, err := pk.Raw()\n\t\tif err != nil {\n\t\t\tgc.logger.Error(\"failed to serialize pk\", zap.Error(err))\n\t\t\tcontinue\n\t\t}\n\n\t\tif _, err := gc.MetadataStore().SendSecret(gc.ctx, pk); err != nil {\n\t\t\tif !errcode.Is(err, errcode.ErrCode_ErrGroupSecretAlreadySentToMember) {\n\t\t\t\tgc.logger.Info(\"secret already sent secret to member\", logutil.PrivateString(\"memberpk\", base64.StdEncoding.EncodeToString(rawPK)))\n\t\t\t\tcontinue\n\t\t\t}\n\t\t} else {\n\t\t\tgc.logger.Info(\"sent secret to existing member\", logutil.PrivateString(\"memberpk\", base64.StdEncoding.EncodeToString(rawPK)))\n\t\t}\n\t}\n}\n\nfunc (gc *GroupContext) TagGroupContextPeers(ipfsCoreAPI ipfsutil.ExtendedCoreAPI, weight int) {\n\tid := gc.Group().GroupIDAsString()\n\n\tchSub1, err := gc.metadataStore.EventBus().Subscribe(new(stores.EventNewPeer))\n\tif err != nil {\n\t\tgc.logger.Warn(\"unable to subscribe to metadata event new peer\")\n\t\treturn\n\t}\n\n\tchSub2, err := gc.messageStore.EventBus().Subscribe(new(stores.EventNewPeer))\n\tif err != nil {\n\t\tgc.logger.Warn(\"unable to subscribe to message event new peer\")\n\t\treturn\n\t}\n\n\tgo func() {\n\t\tdefer chSub1.Close()\n\t\tdefer chSub2.Close()\n\n\t\tfor {\n\t\t\tvar e any\n\n\t\t\tselect {\n\t\t\tcase e = <-chSub1.Out():\n\t\t\tcase e = <-chSub2.Out():\n\t\t\tcase <-gc.ctx.Done():\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tevt := e.(stores.EventNewPeer)\n\n\t\t\ttag := fmt.Sprintf(\"grp_%s\", id)\n\t\t\tgc.logger.Debug(\"new peer of interest\", logutil.PrivateStringer(\"peer\", evt.Peer), zap.String(\"tag\", tag), zap.Int(\"score\", weight))\n\t\t\tipfsCoreAPI.ConnMgr().TagPeer(evt.Peer, tag, weight)\n\t\t}\n\t}()\n}\n\nfunc (gc *GroupContext) WaitForDeviceAdded(ctx context.Context, devicePK crypto.PubKey) (found chan struct{}) {\n\tgc.muDevicesAdded.Lock()\n\tdefer gc.muDevicesAdded.Unlock()\n\n\trawpk, err := devicePK.Raw()\n\tif err != nil {\n\t\tgc.logger.Error(\"unable to get raw public key\", zap.Error(err))\n\t\treturn\n\t}\n\n\tk := string(rawpk)\n\tvar ok bool\n\tif found, ok = gc.devicesAdded[k]; ok {\n\t\treturn\n\t}\n\n\tgroupPublicKey, err := gc.group.GetPubKey()\n\tif err != nil {\n\t\tgc.logger.Error(\"unable to get group public key\", zap.Error(err))\n\t\treturn\n\t}\n\n\tfound = make(chan struct{})\n\tif gc.secretStore.IsChainKeyKnownForDevice(ctx, groupPublicKey, devicePK) {\n\t\tclose(found)\n\t\treturn\n\t}\n\n\tgc.devicesAdded[k] = found\n\treturn\n}\n\nfunc (gc *GroupContext) notifyDeviceAdded(dPK []byte) {\n\tgc.muDevicesAdded.Lock()\n\tk := string(dPK)\n\tif cc, ok := gc.devicesAdded[k]; ok {\n\t\tclose(cc)\n\t\tdelete(gc.devicesAdded, k)\n\t}\n\tgc.muDevicesAdded.Unlock()\n}\n"
  },
  {
    "path": "iface_account.go",
    "content": "package weshnet\n\nimport (\n\t\"github.com/libp2p/go-libp2p/core/crypto\"\n\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n\t\"berty.tech/weshnet/v2/pkg/secretstore\"\n)\n\ntype AccountKeys interface {\n\tAccountPrivKey() (crypto.PrivKey, error)\n\tAccountProofPrivKey() (crypto.PrivKey, error)\n\tDevicePrivKey() (crypto.PrivKey, error)\n\tContactGroupPrivKey(pk crypto.PubKey) (crypto.PrivKey, error)\n\tMemberDeviceForGroup(g *protocoltypes.Group) (secretstore.OwnMemberDevice, error)\n}\n"
  },
  {
    "path": "infra/.gitignore",
    "content": "!.env\n"
  },
  {
    "path": "infra/README.md",
    "content": "# How to Deploy Weshnet Infrastructure\n\nThis guide explains how to set up the essential services for a Weshnet network infrastructure, including rendez-vous points, emitter.io, and relay services.\n\n## Prerequisites\n\nBefore starting the deployment, ensure you have the following tools installed on your system:\n\n1. **Docker**: Required to run all services in containers\n   - For Ubuntu/Debian: `sudo apt update && sudo apt install docker.io docker-compose`\n   - For macOS: Download and install Docker Desktop from [docker.com](https://www.docker.com/products/docker-desktop)\n   - For Windows: Download and install Docker Desktop from [docker.com](https://www.docker.com/products/docker-desktop)\n\n2. **Make**: Used to simplify deployment commands\n   - For Ubuntu/Debian: `sudo apt install make`\n   - For macOS: Install Xcode Command Line Tools with `xcode-select --install`\n   - For Windows: Install via [Chocolatey](https://chocolatey.org/) with `choco install make`\n\nVerify installations with `docker --version` and `make --version`.\n\n## Service Overview\n\nWeshnet relies on three main components to facilitate peer-to-peer communication:\n\n1. **Rendez-vous Point (RDVP)**: Acts as a meeting point for peers to discover each other on the network. It helps peers establish connections without needing to know each other's exact network location beforehand.\n\n2. **Emitter.io**: Provides a pub/sub messaging system that allows peers to broadcast their presence and receive notifications about other peers. This service facilitates real-time communication and discovery within the network.\n\n3. **Relay Service**: Helps peers connect when they're behind NATs or firewalls. It relays traffic between peers that cannot establish direct connections, ensuring connectivity even in challenging network environments.\n\n## Rendez-vous Point and Emitter.io Services\n\n### Setting Up Rendez-vous Point\n\n1. Generate a new private key for the rendez-vous point service:\n   ```sh\n   cd rdvp\n   docker run --rm --entrypoint rdvp bertytech/berty:kubo-v0.29.0 genkey\n   ```\n\n2. When the command completes, you'll receive a key in this format:\n   `CAESQHW91QjcGJN1RrIXtzCf8aC5EHCIB2Q+CSJ6KI68E7WLn49INScVKtToDjCMk4TxnncKWFcys59TjCgu8yBDOD8=`\n\n3. Copy this key to the `RDVP_PK` variable in your `.env` file.\n   \n4. Add your public IP address to the `ANNOUNCE_SERVER` variable in the same file.\n\n### Setting Up Emitter.io\n\n1. Generate a license and secret key for emitter.io:\n   ```sh\n   cd rdvp\n   docker run --rm emitter/server:v3.1\n   ```\n\n2. From the output, copy:\n   - The license to the `EMITTER_LICENSE` variable\n   - The secret key to the `EMITTER_SECRET_KEY` variable in your `.env` file\n\n### Starting the Services\n\nOnce your configuration is complete, start both services with:\n\n```sh\nmake up\n```\n\n### Configuring Your App to Use the Services\n\nPrint the multiaddress of the rendez-vous point service with:\n```sh\ndocker compose logs rdvp | grep maddr\n```\n\nFor mobile, you can prefer the \"quic\" multiaddress which looks something like `/ip4/51.15.25.200/udp/4040/quic-v1/p2p/12D3KooWPFQYmKg3KqZkeXyhwTBhpDu1cWNE8VruyxiMiroStNqh` .\n\nTo configure Berty Messenger, click the user icon to open Settings. Click Network. Click Rendezvous Point Nodes. Click the + to add a node.\n\nTo configure your Wesh app, create the service client with the `WithP2PRdvpMaddrs` option. For example:\n```go\n\tclient1, err := weshnet.NewPersistentServiceClient(\"data1\", weshnet.WithP2PRdvpMaddrs([]string{\n\t\t\"/ip4/51.15.25.200/udp/4040/quic-v1/p2p/12D3KooWPFQYmKg3KqZkeXyhwTBhpDu1cWNE8VruyxiMiroStNq\",\n\t}))\n```\n\n## Relay Service\n\nThe relay service facilitates peer connections through NATs and firewalls.\n\n### Configuration and Deployment\n\n1. Edit the relay configuration file:\n   - Open `relay/config.json`\n   - Update the `Network/AnnounceAddrs` section with your public IP address\n\n2. Deploy your relay:\n   ```sh\n   cd relay\n   make build  # Build the relay Docker image\n   make up     # Start the relay service\n   ```\n\n### Configuring Your App to Use the Service\n\nPrint the multiaddress of the relay service with:\n```sh\ndocker compose logs relay | grep -A 3 \"Public Addresses\"\n```\n\nFor mobile, you can prefer the \"quic\" multiaddress which looks something like `/ip4/51.15.25.200/udp/6363/quic/p2p/12D3KooWKjkkYVJg9RtQCiuV8bKheYB5sgVWSpo6LVyoRHtMZXCF` .\n\nTo configure Berty Messenger, click the user icon to open Settings. Click Network. Click Relay Nodes. Click the + to add a node.\n\nTo configure your Wesh app, create the service client with the `WithP2PStaticRelays` option. For example:\n```go\n\tclient1, err := weshnet.NewPersistentServiceClient(\"data1\", weshnet.WithP2PStaticRelays([]string{\n\t\t\"/ip4/51.15.25.200/udp/6363/quic/p2p/12D3KooWKjkkYVJg9RtQCiuV8bKheYB5sgVWSpo6LVyoRHtMZXCF\",\n\t}))\n```\n\n## Verifying Your Deployment\n\nAfter deployment, you can verify your services are running correctly by checking:\n- Logs for each service\n- Network connectivity through the announced addresses\n- Peer connections through your infrastructure\n\n## Troubleshooting\n\nIf you encounter issues:\n- Check that ports are properly forwarded on your network\n- Verify your public IP is correctly configured in all services\n- Ensure Docker has sufficient resources allocated\n- Review service logs for specific error messages\n"
  },
  {
    "path": "infra/rdvp/.env",
    "content": "# Rendez-vous point public key\nRDVP_PK=\n# External IP address\nANNOUNCE_SERVER=\n\n# Emitter.io secret key\nEMITTER_SECRET_KEY=\n# Emitter.io license key\nEMITTER_LICENSE=\n"
  },
  {
    "path": "infra/rdvp/Makefile",
    "content": ".PHONY: all\nall: up ps logs\n\nup:\n\tdocker compose up -d\n\nlogs:\n\tdocker compose logs --tail=100 -f\n\ndown ps:\n\tdocker compose $@\n\ngenkey:\n\techo RDVP_PK=`docker compose run server genkey` > .env\n\nip:\n\tcurl ifconfig.co\n"
  },
  {
    "path": "infra/rdvp/docker-compose.yml",
    "content": "version: \"3.7\"\n\nservices:\n  rdvp:\n    image: bertytech/berty:kubo-v0.29.0\n    restart: on-failure\n    environment:\n      - RDVP_PK\n      - EMITTER_SECRET_KEY\n    network_mode: bridge\n    entrypoint: rdvp\n    links:\n      - emitter\n    expose:\n      - 8888\n    ports:\n      - 4040:4040\n      - 4040:4040/udp\n    command:\n      - serve\n      - \"-log.format=json\"\n      - \"-log.filters=debug+:*\"\n      - \"--db=:memory:\"\n      - \"--pk=$RDVP_PK\"\n      - \"-metrics=:8888\"\n      - \"-l=/ip4/0.0.0.0/tcp/4040,/ip4/0.0.0.0/udp/4040/quic-v1\"\n      - \"-announce=/ip4/${ANNOUNCE_SERVER}/tcp/4040,/ip4/${ANNOUNCE_SERVER}/udp/4040/quic-v1\"\n      - \"-emitter-admin-key=$EMITTER_SECRET_KEY\"\n      - \"-emitter-public-addr=tcp://${ANNOUNCE_SERVER}:9494\"\n      - \"-emitter-server=tcp://emitter:9494\"\n\n  emitter:\n    image: emitter/server:v3.1\n    container_name: emitter\n    restart: unless-stopped\n    network_mode: bridge\n    ports:\n      - 9494:9494\n    expose:\n      - 9494\n      # - 4000 # for cluster usage\n    environment:\n      - EMITTER_LICENSE\n      - EMITTER_LISTEN=:9494\n"
  },
  {
    "path": "infra/relay/Dockerfile",
    "content": "# builder\nFROM golang:1.19-alpine3.16 as builder\nMAINTAINER gfanton <8671905+gfanton@users.noreply.github.com>\n\n\nARG GIT_REPOS=https://github.com/libp2p/go-libp2p-relay-daemon.git\nARG GIT_TAG=v0.3.0\n\nRUN apk add --no-cache git\nRUN git clone --depth 1 --branch \"${GIT_TAG}\" \"${GIT_REPOS}\" /app\n\nWORKDIR /app\n\nRUN go build -o /go/bin/daemon -v -ldflags=\"-s -w\" -v ./cmd/libp2p-relay-daemon\n\n# runner\nFROM alpine:3.16\n\nCOPY --from=builder /go/bin/daemon /usr/local/bin\n\nENTRYPOINT [\"/usr/local/bin/daemon\"]\n"
  },
  {
    "path": "infra/relay/Makefile",
    "content": "build:\n\ttar -czh . | docker build -t relay - # need to tar because of the symlink\n.PHONY: build\n\nup:\n\tdocker compose up -d\n\nlogs:\n\tdocker compose logs --tail=100 -f\n\ndown ps:\n\tdocker compose $@\n"
  },
  {
    "path": "infra/relay/config.json",
    "content": "{\n  \"RelayV2\": {\n    \"Enabled\": true,\n    \"Resources\": {\n      \"Limit\": null\n    }\n  },\n  \"RelayV1\": {\n    \"Enabled\": true,\n    \"Resources\": {\n      \"MaxCircuits\": 2048,\n      \"MaxCircuitsPerPeer\": 128,\n      \"BufferSize\": 4096\n    }\n  },\n  \"Network\": {\n    \"AnnounceAddrs\": [\n      \"/ip4/<ip_address>/tcp/6363\",\n      \"/ip4/<ip_address>/udp/6363/quic\"\n    ],\n    \"ListenAddrs\": [\n      \"/ip4/0.0.0.0/udp/6363/quic\",\n      \"/ip6/::/udp/6363/quic\",\n      \"/ip4/0.0.0.0/tcp/6363\",\n      \"/ip6/::/tcp/6363\"\n    ]\n  }\n}\n"
  },
  {
    "path": "infra/relay/docker-compose.yml",
    "content": "version: '3.7'\n\nservices:\n  relay:\n    image: relay\n    restart: on-failure\n    volumes:\n      - ./config.json:/etc/daemon-config.json\n      - ./data:/etc/daemon\n    command:\n      - -id=/etc/daemon/id.key\n      - -config=/etc/daemon-config.json\n    ports:\n      - 6363:6363\n      - 6363:6363/udp\n"
  },
  {
    "path": "internal/benchmark/benchmark_test.go",
    "content": "package benchmark\n\nimport (\n\t\"testing\"\n)\n\n// BenchmarkScenario is a benchmark for the scenario\n// FIXME: previous benchmark used Berty Messenger dependencies, we need to find a way to benchmark without them\nfunc BenchmarkScenario(b *testing.B) {\n}\n"
  },
  {
    "path": "internal/bertyversion/example_test.go",
    "content": "package bertyversion_test\n"
  },
  {
    "path": "internal/bertyversion/version.go",
    "content": "package bertyversion\n\nvar (\n\tVersion = \"n/a\"\n\tVcsRef  = \"n/a\"\n)\n"
  },
  {
    "path": "internal/datastoreutil/consts.go",
    "content": "package datastoreutil\n\nconst (\n\tNamespaceMessageKeystore = \"messages_keystore\"\n)\n"
  },
  {
    "path": "internal/datastoreutil/datastore_namespaced.go",
    "content": "package datastoreutil\n\nimport (\n\tds \"github.com/ipfs/go-datastore\"\n\t\"github.com/ipfs/go-datastore/keytransform\"\n)\n\ntype namespacedDatastore struct {\n\tds.Batching\n}\n\nfunc (n *namespacedDatastore) Close() error {\n\t// noop\n\treturn nil\n}\n\nfunc NewNamespacedDatastore(child ds.Datastore, prefix ds.Key) ds.Batching {\n\treturn &namespacedDatastore{Batching: keytransform.Wrap(child, keytransform.PrefixTransform{Prefix: prefix})}\n}\n"
  },
  {
    "path": "internal/handshake/doc.go",
    "content": "// Package handshake implements a capability-based handshake.\n//\n// Handshake Sequence Diagram:\n// ---------------------------\n// Handshake vastely inspired by Scuttlebutt's Capability-based Handshake\n// https://scuttlebot.io/more/protocols/shs.pdf\n//\n//   - a, b are ephemeral key pairs generated by respectively Requester and\n//     Responder. Ephemeral keys are used for one handshake only and then\n//     discarded. They guarantee the freshness of the messages and avoid\n//     replay attacks.\n//\n//   - A, B are the Account IDs of respectively Requester and Responder.\n//\n//   - a.b denotes a secret derived from the two keys a and b.\n//\n//   - | is the concatenation operator.\n//\n//   - box[a.b](content) denotes the encryption of content using Nacl box\n//     with a.b as key.\n//\n//   - sig[A](content) denotes the signature of content verified by A.\n//\n//     +-----------+                       +-----------+\n//     | Requester |                       | Responder |\n//     +-----------+                       +-----------+\n//     | ---------------------\\            |\n//     |-| 1. Requester Hello |            |\n//     | |--------------------|            |\n//     |                                   |\n//     | a                                 |\n//     |---------------------------------->|\n//     |            ---------------------\\ |\n//     |            | 2. Responder Hello |-|\n//     |            |--------------------| |\n//     |                                   |\n//     |                                 b |\n//     |<----------------------------------|\n//     | ----------------------------\\     |\n//     |-| 3. Requester Authenticate |     |\n//     | |---------------------------|     |\n//     |                                   |\n//     | box[a.b|a.B](A,sig[A](a.b))       |\n//     |---------------------------------->|\n//     |           ----------------------\\ |\n//     |           | 4. Responder Accept |-|\n//     |           |---------------------| |\n//     |                                   |\n//     |         box[a.b|A.B](sig[B](a.b)) |\n//     |<----------------------------------|\n//     | ---------------------------\\      |\n//     |-| 5. Requester Acknowledge |      |\n//     | |--------------------------|      |\n//     |                                   |\n//     | ok                                |\n//     |---------------------------------->|\n//     |                                   |\n//\n// See the documentation at https://berty.tech/protocol for more information.\npackage handshake\n"
  },
  {
    "path": "internal/handshake/handshake.go",
    "content": "package handshake\n\nimport (\n\tcrand \"crypto/rand\"\n\t\"encoding/base64\"\n\n\tp2pcrypto \"github.com/libp2p/go-libp2p/core/crypto\"\n\t\"golang.org/x/crypto/nacl/box\"\n\n\t\"berty.tech/weshnet/v2/pkg/cryptoutil\"\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n\t\"berty.tech/weshnet/v2/pkg/protoio\"\n\t\"berty.tech/weshnet/v2/pkg/tyber\"\n)\n\n// Constant nonces\nvar (\n\tnonceRequesterAuthenticate = [cryptoutil.NonceSize]byte{1}\n\tnonceResponderAccept       = [cryptoutil.NonceSize]byte{2}\n)\n\n// Common struct and methods\ntype handshakeContext struct {\n\treader          protoio.Reader\n\twriter          protoio.Writer\n\townAccountID    p2pcrypto.PrivKey\n\tpeerAccountID   p2pcrypto.PubKey\n\townEphemeral    *[cryptoutil.KeySize]byte\n\tpeerEphemeral   *[cryptoutil.KeySize]byte\n\tsharedEphemeral *[cryptoutil.KeySize]byte\n}\n\nfunc (hc *handshakeContext) toTyberStepMutator() tyber.StepMutator {\n\treturn func(s tyber.Step) tyber.Step {\n\t\tif hc == nil {\n\t\t\treturn s\n\t\t}\n\t\tif hc.peerAccountID != nil {\n\t\t\tif cpkb, err := hc.peerAccountID.Raw(); err == nil {\n\t\t\t\ts.Details = append(s.Details, tyber.Detail{Name: \"ContactPublicKey\", Description: base64.RawURLEncoding.EncodeToString(cpkb)})\n\t\t\t}\n\t\t}\n\t\tfor key, val := range map[string]*[cryptoutil.KeySize]byte{\n\t\t\t\"OwnEphemeral\":    hc.ownEphemeral,\n\t\t\t\"PeerEphemeral\":   hc.peerEphemeral,\n\t\t\t\"SharedEphemeral\": hc.sharedEphemeral,\n\t\t} {\n\t\t\tif val != nil {\n\t\t\t\ts.Details = append(s.Details, tyber.Detail{\n\t\t\t\t\tName:        key,\n\t\t\t\t\tDescription: base64.RawURLEncoding.EncodeToString(val[:]),\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t\treturn s\n\t}\n}\n\n// Generates own Ephemeral key pair and send pub key to peer\nfunc (hc *handshakeContext) generateOwnEphemeralAndSendPubKey() error {\n\t// Generate own Ephemeral key pair\n\townEphemeralPub, ownEphemeralPriv, err := box.GenerateKey(crand.Reader)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err)\n\t}\n\n\t// Set own Ephemeral priv key in Handshake Context\n\thc.ownEphemeral = ownEphemeralPriv\n\n\t// Send own Ephemeral pub key to peer\n\thello := HelloPayload{EphemeralPubKey: ownEphemeralPub[:]}\n\n\tif err := hc.writer.WriteMsg(&hello); err != nil {\n\t\treturn errcode.ErrCode_ErrStreamWrite.Wrap(err)\n\t}\n\n\treturn nil\n}\n\n// Receives peer's Ephemeral pub key\nfunc (hc *handshakeContext) receivePeerEphemeralPubKey() error {\n\tvar err error\n\n\t// Receive peer's Ephemeral pub key\n\thello := HelloPayload{}\n\tif err := hc.reader.ReadMsg(&hello); err != nil {\n\t\treturn errcode.ErrCode_ErrStreamRead.Wrap(err)\n\t}\n\n\t// Set peer's Ephemeral pub key in Handshake Context\n\thc.peerEphemeral, err = cryptoutil.KeySliceToArray(hello.EphemeralPubKey)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\treturn nil\n}\n\n// Computes box key for step 3 (Requester Authenticate): box[a.b|a.B]\nfunc (hc *handshakeContext) computeRequesterAuthenticateBoxKey(asRequester bool) (*[cryptoutil.KeySize]byte, error) {\n\tvar sharedReqEphemeralRespAccountID [cryptoutil.KeySize]byte\n\n\t// If this function was called by the requester\n\tif asRequester {\n\t\t// Convert Ed25519 peer's AccountID key to X25519 key\n\t\tmongPeerAccountID, err := cryptoutil.EdwardsToMontgomeryPub(hc.peerAccountID)\n\t\tif err != nil {\n\t\t\treturn nil, errcode.ErrCode_ErrCryptoKeyConversion.Wrap(err)\n\t\t}\n\n\t\t// Compute shared key from own Ephemeral key and peer's AccountID key\n\t\tbox.Precompute(\n\t\t\t&sharedReqEphemeralRespAccountID,\n\t\t\tmongPeerAccountID,\n\t\t\thc.ownEphemeral,\n\t\t)\n\t} else {\n\t\t// Convert Ed25519 own AccountID key to X25519 key\n\t\tmongOwnAccountID, err := cryptoutil.EdwardsToMontgomeryPriv(hc.ownAccountID)\n\t\tif err != nil {\n\t\t\treturn nil, errcode.ErrCode_ErrCryptoKeyConversion.Wrap(err)\n\t\t}\n\n\t\t// Compute shared key from peer's Ephemeral key and own AccountID key\n\t\tbox.Precompute(\n\t\t\t&sharedReqEphemeralRespAccountID,\n\t\t\thc.peerEphemeral,\n\t\t\tmongOwnAccountID,\n\t\t)\n\t}\n\n\t// Concatenate both shared keys and hash them using sha256\n\tboxKey := cryptoutil.ConcatAndHashSha256(\n\t\thc.sharedEphemeral[:],\n\t\tsharedReqEphemeralRespAccountID[:],\n\t)\n\n\treturn boxKey, nil\n}\n\n// Computes box key for step 4 (Responder Accept): box[a.b|A.B]\nfunc (hc *handshakeContext) computeResponderAcceptBoxKey() (*[cryptoutil.KeySize]byte, error) {\n\tvar sharedAccountID [cryptoutil.KeySize]byte\n\n\t// Convert Ed25519 AccountID keys to X25519 keys\n\tmongOwnAccountID, mongPeerAccountID, err := cryptoutil.EdwardsToMontgomery(\n\t\thc.ownAccountID,\n\t\thc.peerAccountID,\n\t)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrCryptoKeyConversion.Wrap(err)\n\t}\n\n\t// Compute shared key from AccountID keys (X25519 converted)\n\tbox.Precompute(&sharedAccountID, mongPeerAccountID, mongOwnAccountID)\n\n\t// Concatenate both shared keys and hash them using sha256\n\tboxKey := cryptoutil.ConcatAndHashSha256(\n\t\thc.sharedEphemeral[:],\n\t\tsharedAccountID[:],\n\t)\n\n\treturn boxKey, nil\n}\n"
  },
  {
    "path": "internal/handshake/handshake.pb.go",
    "content": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.34.2\n// \tprotoc        (unknown)\n// source: handshake/handshake.proto\n\npackage handshake\n\nimport (\n\tprotoreflect \"google.golang.org/protobuf/reflect/protoreflect\"\n\tprotoimpl \"google.golang.org/protobuf/runtime/protoimpl\"\n\treflect \"reflect\"\n\tsync \"sync\"\n)\n\nconst (\n\t// Verify that this generated code is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)\n\t// Verify that runtime/protoimpl is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)\n)\n\ntype BoxEnvelope struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tBox []byte `protobuf:\"bytes,1,opt,name=box,proto3\" json:\"box,omitempty\"`\n}\n\nfunc (x *BoxEnvelope) Reset() {\n\t*x = BoxEnvelope{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_handshake_handshake_proto_msgTypes[0]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *BoxEnvelope) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*BoxEnvelope) ProtoMessage() {}\n\nfunc (x *BoxEnvelope) ProtoReflect() protoreflect.Message {\n\tmi := &file_handshake_handshake_proto_msgTypes[0]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use BoxEnvelope.ProtoReflect.Descriptor instead.\nfunc (*BoxEnvelope) Descriptor() ([]byte, []int) {\n\treturn file_handshake_handshake_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *BoxEnvelope) GetBox() []byte {\n\tif x != nil {\n\t\treturn x.Box\n\t}\n\treturn nil\n}\n\ntype HelloPayload struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tEphemeralPubKey []byte `protobuf:\"bytes,1,opt,name=ephemeral_pub_key,json=ephemeralPubKey,proto3\" json:\"ephemeral_pub_key,omitempty\"`\n}\n\nfunc (x *HelloPayload) Reset() {\n\t*x = HelloPayload{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_handshake_handshake_proto_msgTypes[1]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *HelloPayload) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*HelloPayload) ProtoMessage() {}\n\nfunc (x *HelloPayload) ProtoReflect() protoreflect.Message {\n\tmi := &file_handshake_handshake_proto_msgTypes[1]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use HelloPayload.ProtoReflect.Descriptor instead.\nfunc (*HelloPayload) Descriptor() ([]byte, []int) {\n\treturn file_handshake_handshake_proto_rawDescGZIP(), []int{1}\n}\n\nfunc (x *HelloPayload) GetEphemeralPubKey() []byte {\n\tif x != nil {\n\t\treturn x.EphemeralPubKey\n\t}\n\treturn nil\n}\n\ntype RequesterAuthenticatePayload struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tRequesterAccountId  []byte `protobuf:\"bytes,1,opt,name=requester_account_id,json=requesterAccountId,proto3\" json:\"requester_account_id,omitempty\"`\n\tRequesterAccountSig []byte `protobuf:\"bytes,2,opt,name=requester_account_sig,json=requesterAccountSig,proto3\" json:\"requester_account_sig,omitempty\"`\n}\n\nfunc (x *RequesterAuthenticatePayload) Reset() {\n\t*x = RequesterAuthenticatePayload{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_handshake_handshake_proto_msgTypes[2]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *RequesterAuthenticatePayload) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*RequesterAuthenticatePayload) ProtoMessage() {}\n\nfunc (x *RequesterAuthenticatePayload) ProtoReflect() protoreflect.Message {\n\tmi := &file_handshake_handshake_proto_msgTypes[2]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use RequesterAuthenticatePayload.ProtoReflect.Descriptor instead.\nfunc (*RequesterAuthenticatePayload) Descriptor() ([]byte, []int) {\n\treturn file_handshake_handshake_proto_rawDescGZIP(), []int{2}\n}\n\nfunc (x *RequesterAuthenticatePayload) GetRequesterAccountId() []byte {\n\tif x != nil {\n\t\treturn x.RequesterAccountId\n\t}\n\treturn nil\n}\n\nfunc (x *RequesterAuthenticatePayload) GetRequesterAccountSig() []byte {\n\tif x != nil {\n\t\treturn x.RequesterAccountSig\n\t}\n\treturn nil\n}\n\ntype ResponderAcceptPayload struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tResponderAccountSig []byte `protobuf:\"bytes,1,opt,name=responder_account_sig,json=responderAccountSig,proto3\" json:\"responder_account_sig,omitempty\"`\n}\n\nfunc (x *ResponderAcceptPayload) Reset() {\n\t*x = ResponderAcceptPayload{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_handshake_handshake_proto_msgTypes[3]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ResponderAcceptPayload) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ResponderAcceptPayload) ProtoMessage() {}\n\nfunc (x *ResponderAcceptPayload) ProtoReflect() protoreflect.Message {\n\tmi := &file_handshake_handshake_proto_msgTypes[3]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ResponderAcceptPayload.ProtoReflect.Descriptor instead.\nfunc (*ResponderAcceptPayload) Descriptor() ([]byte, []int) {\n\treturn file_handshake_handshake_proto_rawDescGZIP(), []int{3}\n}\n\nfunc (x *ResponderAcceptPayload) GetResponderAccountSig() []byte {\n\tif x != nil {\n\t\treturn x.ResponderAccountSig\n\t}\n\treturn nil\n}\n\ntype RequesterAcknowledgePayload struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tSuccess bool `protobuf:\"varint,1,opt,name=success,proto3\" json:\"success,omitempty\"`\n}\n\nfunc (x *RequesterAcknowledgePayload) Reset() {\n\t*x = RequesterAcknowledgePayload{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_handshake_handshake_proto_msgTypes[4]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *RequesterAcknowledgePayload) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*RequesterAcknowledgePayload) ProtoMessage() {}\n\nfunc (x *RequesterAcknowledgePayload) ProtoReflect() protoreflect.Message {\n\tmi := &file_handshake_handshake_proto_msgTypes[4]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use RequesterAcknowledgePayload.ProtoReflect.Descriptor instead.\nfunc (*RequesterAcknowledgePayload) Descriptor() ([]byte, []int) {\n\treturn file_handshake_handshake_proto_rawDescGZIP(), []int{4}\n}\n\nfunc (x *RequesterAcknowledgePayload) GetSuccess() bool {\n\tif x != nil {\n\t\treturn x.Success\n\t}\n\treturn false\n}\n\nvar File_handshake_handshake_proto protoreflect.FileDescriptor\n\nvar file_handshake_handshake_proto_rawDesc = []byte{\n\t0x0a, 0x19, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x2f, 0x68, 0x61, 0x6e, 0x64,\n\t0x73, 0x68, 0x61, 0x6b, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, 0x68, 0x61, 0x6e,\n\t0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x22, 0x1f, 0x0a, 0x0b, 0x42, 0x6f, 0x78, 0x45, 0x6e, 0x76,\n\t0x65, 0x6c, 0x6f, 0x70, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x62, 0x6f, 0x78, 0x18, 0x01, 0x20, 0x01,\n\t0x28, 0x0c, 0x52, 0x03, 0x62, 0x6f, 0x78, 0x22, 0x3a, 0x0a, 0x0c, 0x48, 0x65, 0x6c, 0x6c, 0x6f,\n\t0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x2a, 0x0a, 0x11, 0x65, 0x70, 0x68, 0x65, 0x6d,\n\t0x65, 0x72, 0x61, 0x6c, 0x5f, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01,\n\t0x28, 0x0c, 0x52, 0x0f, 0x65, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x50, 0x75, 0x62,\n\t0x4b, 0x65, 0x79, 0x22, 0x84, 0x01, 0x0a, 0x1c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65,\n\t0x72, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x50, 0x61, 0x79,\n\t0x6c, 0x6f, 0x61, 0x64, 0x12, 0x30, 0x0a, 0x14, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65,\n\t0x72, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01,\n\t0x28, 0x0c, 0x52, 0x12, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x72, 0x41, 0x63, 0x63,\n\t0x6f, 0x75, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x32, 0x0a, 0x15, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73,\n\t0x74, 0x65, 0x72, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x73, 0x69, 0x67, 0x18,\n\t0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x13, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x72,\n\t0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x69, 0x67, 0x22, 0x4c, 0x0a, 0x16, 0x52, 0x65,\n\t0x73, 0x70, 0x6f, 0x6e, 0x64, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x50, 0x61, 0x79,\n\t0x6c, 0x6f, 0x61, 0x64, 0x12, 0x32, 0x0a, 0x15, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x64, 0x65,\n\t0x72, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x73, 0x69, 0x67, 0x18, 0x01, 0x20,\n\t0x01, 0x28, 0x0c, 0x52, 0x13, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x64, 0x65, 0x72, 0x41, 0x63,\n\t0x63, 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x69, 0x67, 0x22, 0x37, 0x0a, 0x1b, 0x52, 0x65, 0x71, 0x75,\n\t0x65, 0x73, 0x74, 0x65, 0x72, 0x41, 0x63, 0x6b, 0x6e, 0x6f, 0x77, 0x6c, 0x65, 0x64, 0x67, 0x65,\n\t0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65,\n\t0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73,\n\t0x73, 0x42, 0x2a, 0x5a, 0x28, 0x62, 0x65, 0x72, 0x74, 0x79, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x2f,\n\t0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2f, 0x76, 0x32, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72,\n\t0x6e, 0x61, 0x6c, 0x2f, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x62, 0x06, 0x70,\n\t0x72, 0x6f, 0x74, 0x6f, 0x33,\n}\n\nvar (\n\tfile_handshake_handshake_proto_rawDescOnce sync.Once\n\tfile_handshake_handshake_proto_rawDescData = file_handshake_handshake_proto_rawDesc\n)\n\nfunc file_handshake_handshake_proto_rawDescGZIP() []byte {\n\tfile_handshake_handshake_proto_rawDescOnce.Do(func() {\n\t\tfile_handshake_handshake_proto_rawDescData = protoimpl.X.CompressGZIP(file_handshake_handshake_proto_rawDescData)\n\t})\n\treturn file_handshake_handshake_proto_rawDescData\n}\n\nvar file_handshake_handshake_proto_msgTypes = make([]protoimpl.MessageInfo, 5)\nvar file_handshake_handshake_proto_goTypes = []any{\n\t(*BoxEnvelope)(nil),                  // 0: handshake.BoxEnvelope\n\t(*HelloPayload)(nil),                 // 1: handshake.HelloPayload\n\t(*RequesterAuthenticatePayload)(nil), // 2: handshake.RequesterAuthenticatePayload\n\t(*ResponderAcceptPayload)(nil),       // 3: handshake.ResponderAcceptPayload\n\t(*RequesterAcknowledgePayload)(nil),  // 4: handshake.RequesterAcknowledgePayload\n}\nvar file_handshake_handshake_proto_depIdxs = []int32{\n\t0, // [0:0] is the sub-list for method output_type\n\t0, // [0:0] is the sub-list for method input_type\n\t0, // [0:0] is the sub-list for extension type_name\n\t0, // [0:0] is the sub-list for extension extendee\n\t0, // [0:0] is the sub-list for field type_name\n}\n\nfunc init() { file_handshake_handshake_proto_init() }\nfunc file_handshake_handshake_proto_init() {\n\tif File_handshake_handshake_proto != nil {\n\t\treturn\n\t}\n\tif !protoimpl.UnsafeEnabled {\n\t\tfile_handshake_handshake_proto_msgTypes[0].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*BoxEnvelope); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_handshake_handshake_proto_msgTypes[1].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*HelloPayload); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_handshake_handshake_proto_msgTypes[2].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*RequesterAuthenticatePayload); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_handshake_handshake_proto_msgTypes[3].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ResponderAcceptPayload); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_handshake_handshake_proto_msgTypes[4].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*RequesterAcknowledgePayload); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n\ttype x struct{}\n\tout := protoimpl.TypeBuilder{\n\t\tFile: protoimpl.DescBuilder{\n\t\t\tGoPackagePath: reflect.TypeOf(x{}).PkgPath(),\n\t\t\tRawDescriptor: file_handshake_handshake_proto_rawDesc,\n\t\t\tNumEnums:      0,\n\t\t\tNumMessages:   5,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   0,\n\t\t},\n\t\tGoTypes:           file_handshake_handshake_proto_goTypes,\n\t\tDependencyIndexes: file_handshake_handshake_proto_depIdxs,\n\t\tMessageInfos:      file_handshake_handshake_proto_msgTypes,\n\t}.Build()\n\tFile_handshake_handshake_proto = out.File\n\tfile_handshake_handshake_proto_rawDesc = nil\n\tfile_handshake_handshake_proto_goTypes = nil\n\tfile_handshake_handshake_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "internal/handshake/handshake_test.go",
    "content": "package handshake\n\nimport (\n\t\"context\"\n\tcrand \"crypto/rand\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\tp2pcrypto \"github.com/libp2p/go-libp2p/core/crypto\"\n\tp2pnetwork \"github.com/libp2p/go-libp2p/core/network\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/zap\"\n\t\"golang.org/x/crypto/nacl/box\"\n\t\"google.golang.org/protobuf/proto\"\n\n\t\"berty.tech/weshnet/v2/pkg/cryptoutil\"\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n\t\"berty.tech/weshnet/v2/pkg/ipfsutil\"\n\t\"berty.tech/weshnet/v2/pkg/protoio\"\n\t\"berty.tech/weshnet/v2/pkg/testutil\"\n)\n\n// Request init a handshake with the responder\nfunc Request(stream p2pnetwork.Stream, ownAccountID p2pcrypto.PrivKey, peerAccountID p2pcrypto.PubKey) error {\n\treader := protoio.NewDelimitedReader(stream, 2048)\n\twriter := protoio.NewDelimitedWriter(stream)\n\n\treturn RequestUsingReaderWriter(context.TODO(), zap.NewNop(), reader, writer, ownAccountID, peerAccountID)\n}\n\n// Response handle the handshake inited by the requester\nfunc Response(stream p2pnetwork.Stream, ownAccountID p2pcrypto.PrivKey) (p2pcrypto.PubKey, error) {\n\treader := protoio.NewDelimitedReader(stream, 2048)\n\twriter := protoio.NewDelimitedWriter(stream)\n\n\treturn ResponseUsingReaderWriter(context.TODO(), zap.NewNop(), reader, writer, ownAccountID)\n}\n\nfunc TestValidHandshake(t *testing.T) {\n\ttestutil.FilterSpeed(t, testutil.Slow)\n\n\tvar requesterTest requesterTestFunc = func(\n\t\tt *testing.T,\n\t\tstream p2pnetwork.Stream,\n\t\tmh *mockedHandshake,\n\t) {\n\t\tdefer ipfsutil.FullClose(stream)\n\n\t\terr := Request(\n\t\t\tstream,\n\t\t\tmh.requester.accountID,\n\t\t\tmh.responder.accountID.GetPublic(),\n\t\t)\n\t\trequire.NoError(t, err, \"handshake request failed\")\n\t}\n\n\tvar responderTest responderTestFunc = func(\n\t\tt *testing.T,\n\t\tstream p2pnetwork.Stream,\n\t\tmh *mockedHandshake,\n\t\twg *sync.WaitGroup,\n\t) {\n\t\tdefer wg.Done()\n\t\tdefer ipfsutil.FullClose(stream)\n\n\t\tpeerAccountID, err := Response(stream, mh.responder.accountID)\n\t\trequire.NoError(t, err, \"handshake response failed\")\n\n\t\trequire.True(\n\t\t\tt,\n\t\t\tpeerAccountID.Equals(mh.requester.accountID.GetPublic()),\n\t\t\t\"received peerAccountID by responder != requester's AccountID\",\n\t\t)\n\t}\n\n\trunHandshakeTest(t, requesterTest, responderTest)\n}\n\nfunc TestInvalidRequesterHello(t *testing.T) {\n\ttestutil.FilterSpeed(t, testutil.Slow)\n\n\tt.Log(\"Requester interrupts by closing stream\")\n\t{\n\t\tstart := time.Now()\n\n\t\tvar requesterTest requesterTestFunc = func(\n\t\t\t_ *testing.T,\n\t\t\tstream p2pnetwork.Stream,\n\t\t\t_ *mockedHandshake,\n\t\t) {\n\t\t\tipfsutil.FullClose(stream)\n\t\t}\n\n\t\tvar responderTest responderTestFunc = func(\n\t\t\tt *testing.T,\n\t\t\tstream p2pnetwork.Stream,\n\t\t\tmh *mockedHandshake,\n\t\t\twg *sync.WaitGroup,\n\t\t) {\n\t\t\tdefer wg.Done()\n\t\t\tdefer ipfsutil.FullClose(stream)\n\n\t\t\t_, err := Response(stream, mh.responder.accountID)\n\t\t\trequire.Contains(t, errcode.Codes(err), errcode.ErrCode_ErrHandshakeRequesterHello)\n\t\t\trequire.Contains(t, errcode.Codes(err), errcode.ErrCode_ErrStreamRead)\n\t\t}\n\n\t\trunHandshakeTest(t, requesterTest, responderTest)\n\t\tt.Logf(\"\\tduration: %s\", time.Since(start))\n\t}\n}\n\nfunc TestInvalidResponderHello(t *testing.T) {\n\ttestutil.FilterSpeed(t, testutil.Slow)\n\n\tt.Log(\"Responder interrupts by closing stream\")\n\t{\n\t\tstart := time.Now()\n\n\t\tvar requesterTest requesterTestFunc = func(\n\t\t\tt *testing.T,\n\t\t\tstream p2pnetwork.Stream,\n\t\t\tmh *mockedHandshake,\n\t\t) {\n\t\t\tdefer ipfsutil.FullClose(stream)\n\n\t\t\terr := Request(\n\t\t\t\tstream,\n\t\t\t\tmh.requester.accountID,\n\t\t\t\tmh.responder.accountID.GetPublic(),\n\t\t\t)\n\t\t\trequire.Equal(t, errcode.Codes(err), []errcode.ErrCode{errcode.ErrCode_ErrHandshakeResponderHello, errcode.ErrCode_ErrHandshakePeerEphemeralKeyRecv, errcode.ErrCode_ErrStreamRead})\n\t\t}\n\n\t\tvar responderTest responderTestFunc = func(\n\t\t\tt *testing.T,\n\t\t\tstream p2pnetwork.Stream,\n\t\t\tmh *mockedHandshake,\n\t\t\twg *sync.WaitGroup,\n\t\t) {\n\t\t\tdefer wg.Done()\n\n\t\t\thc := newTestHandshakeContext(stream, mh.responder.accountID, nil)\n\n\t\t\terr := hc.receiveRequesterHello()\n\t\t\trequire.NoError(t, err, \"receive RequesterHello failed\")\n\n\t\t\tipfsutil.FullClose(stream)\n\t\t}\n\n\t\trunHandshakeTest(t, requesterTest, responderTest)\n\t\tt.Logf(\"\\tduration: %s\", time.Since(start))\n\t}\n}\n\nfunc TestInvalidRequesterAuthenticate(t *testing.T) {\n\ttestutil.FilterSpeed(t, testutil.Slow)\n\n\tt.Log(\"Requester interrupts by closing stream\")\n\t{\n\t\tstart := time.Now()\n\n\t\tvar requesterTest requesterTestFunc = func(\n\t\t\tt *testing.T,\n\t\t\tstream p2pnetwork.Stream,\n\t\t\tmh *mockedHandshake,\n\t\t) {\n\t\t\thc := newTestHandshakeContext(\n\t\t\t\tstream,\n\t\t\t\tmh.requester.accountID,\n\t\t\t\tmh.responder.accountID.GetPublic(),\n\t\t\t)\n\n\t\t\terr := hc.sendRequesterHello()\n\t\t\trequire.NoError(t, err, \"send RequesterHello failed\")\n\n\t\t\terr = hc.receiveResponderHello()\n\t\t\trequire.NoError(t, err, \"receive ResponderHello failed\")\n\n\t\t\t// Interrupt step by closing stream\n\t\t\tipfsutil.FullClose(stream)\n\t\t}\n\n\t\tvar responderTest responderTestFunc = func(\n\t\t\tt *testing.T,\n\t\t\tstream p2pnetwork.Stream,\n\t\t\tmh *mockedHandshake,\n\t\t\twg *sync.WaitGroup,\n\t\t) {\n\t\t\tdefer wg.Done()\n\t\t\tdefer ipfsutil.FullClose(stream)\n\n\t\t\t_, err := Response(stream, mh.requester.accountID)\n\t\t\trequire.Equal(t, errcode.Codes(err), []errcode.ErrCode{errcode.ErrCode_ErrHandshakeRequesterAuthenticate, errcode.ErrCode_ErrStreamRead})\n\t\t}\n\n\t\trunHandshakeTest(t, requesterTest, responderTest)\n\t\tt.Logf(\"\\tduration: %s\", time.Since(start))\n\t}\n\n\tt.Log(\"Requester sends invalid AccountID\")\n\t{\n\t\tstart := time.Now()\n\n\t\tvar requesterTest requesterTestFunc = func(\n\t\t\tt *testing.T,\n\t\t\tstream p2pnetwork.Stream,\n\t\t\tmh *mockedHandshake,\n\t\t) {\n\t\t\tvar request RequesterAuthenticatePayload\n\n\t\t\thc := newTestHandshakeContext(\n\t\t\t\tstream,\n\t\t\t\tmh.requester.accountID,\n\t\t\t\tmh.responder.accountID.GetPublic(),\n\t\t\t)\n\n\t\t\terr := hc.sendRequesterHello()\n\t\t\trequire.NoError(t, err, \"send RequesterHello failed\")\n\n\t\t\terr = hc.receiveResponderHello()\n\t\t\trequire.NoError(t, err, \"receive ResponderHello failed\")\n\n\t\t\t// Send invalid AccountID\n\t\t\trequest.RequesterAccountId = []byte(\"NotAKey\")\n\t\t\trequest.RequesterAccountSig, err = hc.ownAccountID.Sign(hc.sharedEphemeral[:])\n\t\t\trequire.NoError(t, err, \"sharedEphemeral signing failed\")\n\n\t\t\trequestBytes, err := proto.Marshal(&request)\n\t\t\trequire.NoError(t, err, \"request marshaling failed\")\n\n\t\t\tboxKey, err := hc.computeRequesterAuthenticateBoxKey(true)\n\t\t\trequire.NoError(t, err, \"Requester Authenticate box key gen failed\")\n\n\t\t\tboxContent := box.SealAfterPrecomputation(\n\t\t\t\tnil,\n\t\t\t\trequestBytes,\n\t\t\t\t&nonceRequesterAuthenticate,\n\t\t\t\tboxKey,\n\t\t\t)\n\n\t\t\thc.writer.WriteMsg(&BoxEnvelope{Box: boxContent})\n\n\t\t\tipfsutil.FullClose(stream)\n\t\t}\n\n\t\tvar responderTest responderTestFunc = func(\n\t\t\tt *testing.T,\n\t\t\tstream p2pnetwork.Stream,\n\t\t\tmh *mockedHandshake,\n\t\t\twg *sync.WaitGroup,\n\t\t) {\n\t\t\tdefer wg.Done()\n\t\t\tdefer ipfsutil.FullClose(stream)\n\n\t\t\t_, err := Response(stream, mh.responder.accountID)\n\t\t\trequire.Equal(t, errcode.Codes(err), []errcode.ErrCode{errcode.ErrCode_ErrHandshakeRequesterAuthenticate, errcode.ErrCode_ErrDeserialization})\n\t\t}\n\n\t\trunHandshakeTest(t, requesterTest, responderTest)\n\t\tt.Logf(\"\\tduration: %s\", time.Since(start))\n\t}\n\n\tt.Log(\"Requester sends another AccountID\")\n\t{\n\t\tstart := time.Now()\n\n\t\tvar requesterTest requesterTestFunc = func(\n\t\t\tt *testing.T,\n\t\t\tstream p2pnetwork.Stream,\n\t\t\tmh *mockedHandshake,\n\t\t) {\n\t\t\tvar request RequesterAuthenticatePayload\n\n\t\t\thc := newTestHandshakeContext(\n\t\t\t\tstream,\n\t\t\t\tmh.requester.accountID,\n\t\t\t\tmh.responder.accountID.GetPublic(),\n\t\t\t)\n\n\t\t\terr := hc.sendRequesterHello()\n\t\t\trequire.NoError(t, err, \"send RequesterHello failed\")\n\n\t\t\terr = hc.receiveResponderHello()\n\t\t\trequire.NoError(t, err, \"receive ResponderHello failed\")\n\n\t\t\t// Send another AccountID\n\t\t\t_, wrongAccountIDPub, err := p2pcrypto.GenerateEd25519Key(crand.Reader)\n\t\t\trequire.NoError(t, err, \"wrongAccountID generation failed\")\n\n\t\t\trequest.RequesterAccountId, err = p2pcrypto.MarshalPublicKey(wrongAccountIDPub)\n\t\t\trequire.NoError(t, err, \"wrongAccountID marshaling failed\")\n\n\t\t\trequest.RequesterAccountSig, err = hc.ownAccountID.Sign(hc.sharedEphemeral[:])\n\t\t\trequire.NoError(t, err, \"sharedEphemeral signing failed\")\n\n\t\t\trequestBytes, err := proto.Marshal(&request)\n\t\t\trequire.NoError(t, err, \"request marshaling failed\")\n\n\t\t\tboxKey, err := hc.computeRequesterAuthenticateBoxKey(true)\n\t\t\trequire.NoError(t, err, \"Requester Authenticate box key gen failed\")\n\n\t\t\tboxContent := box.SealAfterPrecomputation(\n\t\t\t\tnil,\n\t\t\t\trequestBytes,\n\t\t\t\t&nonceRequesterAuthenticate,\n\t\t\t\tboxKey,\n\t\t\t)\n\n\t\t\thc.writer.WriteMsg(&BoxEnvelope{Box: boxContent})\n\n\t\t\tipfsutil.FullClose(stream)\n\t\t}\n\n\t\tvar responderTest responderTestFunc = func(\n\t\t\tt *testing.T,\n\t\t\tstream p2pnetwork.Stream,\n\t\t\tmh *mockedHandshake,\n\t\t\twg *sync.WaitGroup,\n\t\t) {\n\t\t\tdefer wg.Done()\n\t\t\tdefer ipfsutil.FullClose(stream)\n\n\t\t\t_, err := Response(stream, mh.responder.accountID)\n\t\t\trequire.Equal(t, errcode.Codes(err), []errcode.ErrCode{errcode.ErrCode_ErrHandshakeRequesterAuthenticate, errcode.ErrCode_ErrCryptoSignatureVerification})\n\t\t}\n\n\t\trunHandshakeTest(t, requesterTest, responderTest)\n\t\tt.Logf(\"\\tduration: %s\", time.Since(start))\n\t}\n\n\tt.Log(\"Requester signs with another AccountID\")\n\t{\n\t\tstart := time.Now()\n\n\t\tvar requesterTest requesterTestFunc = func(\n\t\t\tt *testing.T,\n\t\t\tstream p2pnetwork.Stream,\n\t\t\tmh *mockedHandshake,\n\t\t) {\n\t\t\tvar request RequesterAuthenticatePayload\n\n\t\t\thc := newTestHandshakeContext(\n\t\t\t\tstream,\n\t\t\t\tmh.requester.accountID,\n\t\t\t\tmh.responder.accountID.GetPublic(),\n\t\t\t)\n\n\t\t\terr := hc.sendRequesterHello()\n\t\t\trequire.NoError(t, err, \"send RequesterHello failed\")\n\n\t\t\terr = hc.receiveResponderHello()\n\t\t\trequire.NoError(t, err, \"receive ResponderHello failed\")\n\n\t\t\trequest.RequesterAccountId, err = p2pcrypto.MarshalPublicKey(hc.ownAccountID.GetPublic())\n\t\t\trequire.NoError(t, err, \"ownAccountID marshaling failed\")\n\n\t\t\t// Sign with another AccountID\n\t\t\twrongAccountID, _, err := p2pcrypto.GenerateEd25519Key(crand.Reader)\n\t\t\trequire.NoError(t, err, \"wrongAccountID generation failed\")\n\n\t\t\trequest.RequesterAccountSig, err = wrongAccountID.Sign(hc.sharedEphemeral[:])\n\t\t\trequire.NoError(t, err, \"sharedEphemeral signing failed\")\n\n\t\t\trequestBytes, err := proto.Marshal(&request)\n\t\t\trequire.NoError(t, err, \"request marshaling failed\")\n\n\t\t\tboxKey, err := hc.computeRequesterAuthenticateBoxKey(true)\n\t\t\trequire.NoError(t, err, \"Requester Authenticate box key gen failed\")\n\n\t\t\tboxContent := box.SealAfterPrecomputation(\n\t\t\t\tnil,\n\t\t\t\trequestBytes,\n\t\t\t\t&nonceRequesterAuthenticate,\n\t\t\t\tboxKey,\n\t\t\t)\n\n\t\t\thc.writer.WriteMsg(&BoxEnvelope{Box: boxContent})\n\n\t\t\tipfsutil.FullClose(stream)\n\t\t}\n\n\t\tvar responderTest responderTestFunc = func(\n\t\t\tt *testing.T,\n\t\t\tstream p2pnetwork.Stream,\n\t\t\tmh *mockedHandshake,\n\t\t\twg *sync.WaitGroup,\n\t\t) {\n\t\t\tdefer wg.Done()\n\t\t\tdefer ipfsutil.FullClose(stream)\n\n\t\t\t_, err := Response(stream, mh.responder.accountID)\n\t\t\trequire.Equal(t, errcode.Codes(err), []errcode.ErrCode{errcode.ErrCode_ErrHandshakeRequesterAuthenticate, errcode.ErrCode_ErrCryptoSignatureVerification})\n\t\t}\n\n\t\trunHandshakeTest(t, requesterTest, responderTest)\n\t\tt.Logf(\"\\tduration: %s\", time.Since(start))\n\t}\n\n\tt.Log(\"Requester signs invalid proof\")\n\t{\n\t\tstart := time.Now()\n\n\t\tvar requesterTest requesterTestFunc = func(\n\t\t\tt *testing.T,\n\t\t\tstream p2pnetwork.Stream,\n\t\t\tmh *mockedHandshake,\n\t\t) {\n\t\t\tvar request RequesterAuthenticatePayload\n\n\t\t\thc := newTestHandshakeContext(\n\t\t\t\tstream,\n\t\t\t\tmh.requester.accountID,\n\t\t\t\tmh.responder.accountID.GetPublic(),\n\t\t\t)\n\n\t\t\terr := hc.sendRequesterHello()\n\t\t\trequire.NoError(t, err, \"send RequesterHello failed\")\n\n\t\t\terr = hc.receiveResponderHello()\n\t\t\trequire.NoError(t, err, \"receive ResponderHello failed\")\n\n\t\t\trequest.RequesterAccountId, err = p2pcrypto.MarshalPublicKey(hc.ownAccountID.GetPublic())\n\t\t\trequire.NoError(t, err, \"ownAccountID marshaling failed\")\n\n\t\t\t// Sign invalid proof\n\t\t\trequest.RequesterAccountSig, err = hc.ownAccountID.Sign([]byte(\"WrongProof\"))\n\t\t\trequire.NoError(t, err, \"sharedEphemeral signing failed\")\n\n\t\t\trequestBytes, err := proto.Marshal(&request)\n\t\t\trequire.NoError(t, err, \"request marshaling failed\")\n\n\t\t\tboxKey, err := hc.computeRequesterAuthenticateBoxKey(true)\n\t\t\trequire.NoError(t, err, \"Requester Authenticate box key gen failed\")\n\n\t\t\tboxContent := box.SealAfterPrecomputation(\n\t\t\t\tnil,\n\t\t\t\trequestBytes,\n\t\t\t\t&nonceRequesterAuthenticate,\n\t\t\t\tboxKey,\n\t\t\t)\n\n\t\t\thc.writer.WriteMsg(&BoxEnvelope{Box: boxContent})\n\n\t\t\tipfsutil.FullClose(stream)\n\t\t}\n\n\t\tvar responderTest responderTestFunc = func(\n\t\t\tt *testing.T,\n\t\t\tstream p2pnetwork.Stream,\n\t\t\tmh *mockedHandshake,\n\t\t\twg *sync.WaitGroup,\n\t\t) {\n\t\t\tdefer wg.Done()\n\t\t\tdefer ipfsutil.FullClose(stream)\n\n\t\t\t_, err := Response(stream, mh.responder.accountID)\n\t\t\trequire.Equal(t, errcode.Codes(err), []errcode.ErrCode{errcode.ErrCode_ErrHandshakeRequesterAuthenticate, errcode.ErrCode_ErrCryptoSignatureVerification})\n\t\t}\n\n\t\trunHandshakeTest(t, requesterTest, responderTest)\n\t\tt.Logf(\"\\tduration: %s\", time.Since(start))\n\t}\n\n\tt.Log(\"Requester sends invalid request content\")\n\t{\n\t\tstart := time.Now()\n\n\t\tvar requesterTest requesterTestFunc = func(\n\t\t\tt *testing.T,\n\t\t\tstream p2pnetwork.Stream,\n\t\t\tmh *mockedHandshake,\n\t\t) {\n\t\t\thc := newTestHandshakeContext(\n\t\t\t\tstream,\n\t\t\t\tmh.requester.accountID,\n\t\t\t\tmh.responder.accountID.GetPublic(),\n\t\t\t)\n\n\t\t\terr := hc.sendRequesterHello()\n\t\t\trequire.NoError(t, err, \"send RequesterHello failed\")\n\n\t\t\terr = hc.receiveResponderHello()\n\t\t\trequire.NoError(t, err, \"receive ResponderHello failed\")\n\n\t\t\t// Send invalid request content\n\t\t\trequestBytes := []byte(\"WrongRequestContent\")\n\n\t\t\tboxKey, err := hc.computeRequesterAuthenticateBoxKey(true)\n\t\t\trequire.NoError(t, err, \"Requester Authenticate box key gen failed\")\n\n\t\t\tboxContent := box.SealAfterPrecomputation(\n\t\t\t\tnil,\n\t\t\t\trequestBytes,\n\t\t\t\t&nonceRequesterAuthenticate,\n\t\t\t\tboxKey,\n\t\t\t)\n\n\t\t\thc.writer.WriteMsg(&BoxEnvelope{Box: boxContent})\n\n\t\t\tipfsutil.FullClose(stream)\n\t\t}\n\n\t\tvar responderTest responderTestFunc = func(\n\t\t\tt *testing.T,\n\t\t\tstream p2pnetwork.Stream,\n\t\t\tmh *mockedHandshake,\n\t\t\twg *sync.WaitGroup,\n\t\t) {\n\t\t\tdefer wg.Done()\n\t\t\tdefer ipfsutil.FullClose(stream)\n\n\t\t\t_, err := Response(stream, mh.responder.accountID)\n\t\t\trequire.Equal(t, errcode.Codes(err), []errcode.ErrCode{errcode.ErrCode_ErrHandshakeRequesterAuthenticate, errcode.ErrCode_ErrDeserialization})\n\t\t}\n\n\t\trunHandshakeTest(t, requesterTest, responderTest)\n\t\tt.Logf(\"\\tduration: %s\", time.Since(start))\n\t}\n\n\tt.Log(\"Requester seals box using another key\")\n\t{\n\t\tstart := time.Now()\n\n\t\tvar requesterTest requesterTestFunc = func(\n\t\t\tt *testing.T,\n\t\t\tstream p2pnetwork.Stream,\n\t\t\tmh *mockedHandshake,\n\t\t) {\n\t\t\tvar request RequesterAuthenticatePayload\n\n\t\t\thc := newTestHandshakeContext(\n\t\t\t\tstream,\n\t\t\t\tmh.requester.accountID,\n\t\t\t\tmh.responder.accountID.GetPublic(),\n\t\t\t)\n\n\t\t\terr := hc.sendRequesterHello()\n\t\t\trequire.NoError(t, err, \"send RequesterHello failed\")\n\n\t\t\terr = hc.receiveResponderHello()\n\t\t\trequire.NoError(t, err, \"receive ResponderHello failed\")\n\n\t\t\trequest.RequesterAccountId, err = p2pcrypto.MarshalPublicKey(hc.ownAccountID.GetPublic())\n\t\t\trequire.NoError(t, err, \"ownAccountID marshaling failed\")\n\n\t\t\trequest.RequesterAccountSig, err = hc.ownAccountID.Sign(hc.sharedEphemeral[:])\n\t\t\trequire.NoError(t, err, \"sharedEphemeral signing failed\")\n\n\t\t\trequestBytes, err := proto.Marshal(&request)\n\t\t\trequire.NoError(t, err, \"request marshaling failed\")\n\n\t\t\t// Seal box using another key\n\t\t\twrongBoxKey := &[32]byte{}\n\t\t\tcrand.Read(wrongBoxKey[:])\n\n\t\t\tboxContent := box.SealAfterPrecomputation(\n\t\t\t\tnil,\n\t\t\t\trequestBytes,\n\t\t\t\t&nonceRequesterAuthenticate,\n\t\t\t\twrongBoxKey,\n\t\t\t)\n\n\t\t\thc.writer.WriteMsg(&BoxEnvelope{Box: boxContent})\n\n\t\t\tipfsutil.FullClose(stream)\n\t\t}\n\n\t\tvar responderTest responderTestFunc = func(\n\t\t\tt *testing.T,\n\t\t\tstream p2pnetwork.Stream,\n\t\t\tmh *mockedHandshake,\n\t\t\twg *sync.WaitGroup,\n\t\t) {\n\t\t\tdefer wg.Done()\n\t\t\tdefer ipfsutil.FullClose(stream)\n\n\t\t\t_, err := Response(stream, mh.responder.accountID)\n\t\t\trequire.Equal(t, errcode.Codes(err), []errcode.ErrCode{errcode.ErrCode_ErrHandshakeRequesterAuthenticate, errcode.ErrCode_ErrCryptoDecrypt})\n\t\t}\n\n\t\trunHandshakeTest(t, requesterTest, responderTest)\n\t\tt.Logf(\"\\tduration: %s\", time.Since(start))\n\t}\n\n\tt.Log(\"Requester seals using another nonce\")\n\t{\n\t\tstart := time.Now()\n\n\t\tvar requesterTest requesterTestFunc = func(\n\t\t\tt *testing.T,\n\t\t\tstream p2pnetwork.Stream,\n\t\t\tmh *mockedHandshake,\n\t\t) {\n\t\t\tvar request RequesterAuthenticatePayload\n\n\t\t\thc := newTestHandshakeContext(\n\t\t\t\tstream,\n\t\t\t\tmh.requester.accountID,\n\t\t\t\tmh.responder.accountID.GetPublic(),\n\t\t\t)\n\n\t\t\terr := hc.sendRequesterHello()\n\t\t\trequire.NoError(t, err, \"send RequesterHello failed\")\n\n\t\t\terr = hc.receiveResponderHello()\n\t\t\trequire.NoError(t, err, \"receive ResponderHello failed\")\n\n\t\t\trequest.RequesterAccountId, err = p2pcrypto.MarshalPublicKey(hc.ownAccountID.GetPublic())\n\t\t\trequire.NoError(t, err, \"ownAccountID marshaling failed\")\n\n\t\t\trequest.RequesterAccountSig, err = hc.ownAccountID.Sign(hc.sharedEphemeral[:])\n\t\t\trequire.NoError(t, err, \"sharedEphemeral signing failed\")\n\n\t\t\trequestBytes, err := proto.Marshal(&request)\n\t\t\trequire.NoError(t, err, \"request marshaling failed\")\n\n\t\t\tboxKey, err := hc.computeRequesterAuthenticateBoxKey(true)\n\t\t\trequire.NoError(t, err, \"Requester Authenticate box key gen failed\")\n\n\t\t\t// Seals using another nonce\n\t\t\twrongNonce, err := cryptoutil.GenerateNonce()\n\t\t\trequire.NoError(t, err, \"nonce generation failed\")\n\n\t\t\tboxContent := box.SealAfterPrecomputation(\n\t\t\t\tnil,\n\t\t\t\trequestBytes,\n\t\t\t\twrongNonce,\n\t\t\t\tboxKey,\n\t\t\t)\n\n\t\t\thc.writer.WriteMsg(&BoxEnvelope{Box: boxContent})\n\n\t\t\tipfsutil.FullClose(stream)\n\t\t}\n\n\t\tvar responderTest responderTestFunc = func(\n\t\t\tt *testing.T,\n\t\t\tstream p2pnetwork.Stream,\n\t\t\tmh *mockedHandshake,\n\t\t\twg *sync.WaitGroup,\n\t\t) {\n\t\t\tdefer wg.Done()\n\t\t\tdefer ipfsutil.FullClose(stream)\n\n\t\t\t_, err := Response(stream, mh.responder.accountID)\n\t\t\trequire.Equal(t, errcode.Codes(err), []errcode.ErrCode{errcode.ErrCode_ErrHandshakeRequesterAuthenticate, errcode.ErrCode_ErrCryptoDecrypt})\n\t\t}\n\n\t\trunHandshakeTest(t, requesterTest, responderTest)\n\t\tt.Logf(\"\\tduration: %s\", time.Since(start))\n\t}\n\n\tt.Log(\"Requester sends invalid box content\")\n\t{\n\t\tstart := time.Now()\n\n\t\tvar requesterTest requesterTestFunc = func(\n\t\t\tt *testing.T,\n\t\t\tstream p2pnetwork.Stream,\n\t\t\tmh *mockedHandshake,\n\t\t) {\n\t\t\thc := newTestHandshakeContext(\n\t\t\t\tstream,\n\t\t\t\tmh.requester.accountID,\n\t\t\t\tmh.responder.accountID.GetPublic(),\n\t\t\t)\n\n\t\t\terr := hc.sendRequesterHello()\n\t\t\trequire.NoError(t, err, \"send RequesterHello failed\")\n\n\t\t\terr = hc.receiveResponderHello()\n\t\t\trequire.NoError(t, err, \"receive ResponderHello failed\")\n\n\t\t\t// Send invalid box content\n\t\t\thc.writer.WriteMsg(&BoxEnvelope{Box: []byte(\"WrongBoxContent\")})\n\n\t\t\tipfsutil.FullClose(stream)\n\t\t}\n\n\t\tvar responderTest responderTestFunc = func(\n\t\t\tt *testing.T,\n\t\t\tstream p2pnetwork.Stream,\n\t\t\tmh *mockedHandshake,\n\t\t\twg *sync.WaitGroup,\n\t\t) {\n\t\t\tdefer wg.Done()\n\t\t\tdefer ipfsutil.FullClose(stream)\n\n\t\t\t_, err := Response(stream, mh.responder.accountID)\n\t\t\trequire.Equal(t, errcode.Codes(err), []errcode.ErrCode{errcode.ErrCode_ErrHandshakeRequesterAuthenticate, errcode.ErrCode_ErrCryptoDecrypt})\n\t\t}\n\n\t\trunHandshakeTest(t, requesterTest, responderTest)\n\t\tt.Logf(\"\\tduration: %s\", time.Since(start))\n\t}\n}\n\nfunc TestInvalidResponderAccept(t *testing.T) {\n\ttestutil.FilterSpeed(t, testutil.Slow)\n\n\tt.Log(\"Responder interrupts by closing stream\")\n\t{\n\t\tstart := time.Now()\n\n\t\tvar requesterTest requesterTestFunc = func(\n\t\t\tt *testing.T,\n\t\t\tstream p2pnetwork.Stream,\n\t\t\tmh *mockedHandshake,\n\t\t) {\n\t\t\tdefer ipfsutil.FullClose(stream)\n\n\t\t\terr := Request(\n\t\t\t\tstream,\n\t\t\t\tmh.requester.accountID,\n\t\t\t\tmh.responder.accountID.GetPublic(),\n\t\t\t)\n\t\t\trequire.Equal(t, errcode.Codes(err), []errcode.ErrCode{errcode.ErrCode_ErrHandshakeResponderAccept, errcode.ErrCode_ErrStreamRead})\n\t\t}\n\n\t\tvar responderTest responderTestFunc = func(\n\t\t\tt *testing.T,\n\t\t\tstream p2pnetwork.Stream,\n\t\t\tmh *mockedHandshake,\n\t\t\twg *sync.WaitGroup,\n\t\t) {\n\t\t\tdefer wg.Done()\n\n\t\t\thc := newTestHandshakeContext(stream, mh.responder.accountID, nil)\n\n\t\t\terr := hc.receiveRequesterHello()\n\t\t\trequire.NoError(t, err, \"receive RequesterHello failed\")\n\n\t\t\terr = hc.sendResponderHello()\n\t\t\trequire.NoError(t, err, \"send ResponderHello failed\")\n\n\t\t\terr = hc.receiveRequesterAuthenticate()\n\t\t\trequire.NoError(t, err, \"receive RequesterAuthenticate failed\")\n\n\t\t\tipfsutil.FullClose(stream)\n\t\t}\n\n\t\trunHandshakeTest(t, requesterTest, responderTest)\n\t\tt.Logf(\"\\tduration: %s\", time.Since(start))\n\t}\n\n\tt.Log(\"Responder signs with another AccountID\")\n\t{\n\t\tstart := time.Now()\n\n\t\tvar requesterTest requesterTestFunc = func(\n\t\t\tt *testing.T,\n\t\t\tstream p2pnetwork.Stream,\n\t\t\tmh *mockedHandshake,\n\t\t) {\n\t\t\tdefer ipfsutil.FullClose(stream)\n\n\t\t\terr := Request(\n\t\t\t\tstream,\n\t\t\t\tmh.requester.accountID,\n\t\t\t\tmh.responder.accountID.GetPublic(),\n\t\t\t)\n\t\t\trequire.Equal(t, errcode.Codes(err), []errcode.ErrCode{errcode.ErrCode_ErrHandshakeResponderAccept, errcode.ErrCode_ErrCryptoSignatureVerification})\n\t\t}\n\n\t\tvar responderTest responderTestFunc = func(\n\t\t\tt *testing.T,\n\t\t\tstream p2pnetwork.Stream,\n\t\t\tmh *mockedHandshake,\n\t\t\twg *sync.WaitGroup,\n\t\t) {\n\t\t\tvar response ResponderAcceptPayload\n\n\t\t\tdefer wg.Done()\n\n\t\t\thc := newTestHandshakeContext(stream, mh.responder.accountID, nil)\n\n\t\t\terr := hc.receiveRequesterHello()\n\t\t\trequire.NoError(t, err, \"receive RequesterHello failed\")\n\n\t\t\terr = hc.sendResponderHello()\n\t\t\trequire.NoError(t, err, \"send ResponderHello failed\")\n\n\t\t\terr = hc.receiveRequesterAuthenticate()\n\t\t\trequire.NoError(t, err, \"receive RequesterAuthenticate failed\")\n\n\t\t\t// Sign with another AccountID\n\t\t\twrongAccountID, _, err := p2pcrypto.GenerateEd25519Key(crand.Reader)\n\t\t\trequire.NoError(t, err, \"wrongAccountID generation failed\")\n\n\t\t\tresponse.ResponderAccountSig, err = wrongAccountID.Sign(hc.sharedEphemeral[:])\n\t\t\trequire.NoError(t, err, \"sharedEphemeral signing failed\")\n\n\t\t\tresponseBytes, err := proto.Marshal(&response)\n\t\t\trequire.NoError(t, err, \"response marshaling failed\")\n\n\t\t\tboxKey, err := hc.computeResponderAcceptBoxKey()\n\t\t\trequire.NoError(t, err, \"ResponderAccept Accept box key gen failed\")\n\n\t\t\tboxContent := box.SealAfterPrecomputation(\n\t\t\t\tnil,\n\t\t\t\tresponseBytes,\n\t\t\t\t&nonceResponderAccept,\n\t\t\t\tboxKey,\n\t\t\t)\n\n\t\t\thc.writer.WriteMsg(&BoxEnvelope{Box: boxContent})\n\n\t\t\tipfsutil.FullClose(stream)\n\t\t}\n\n\t\trunHandshakeTest(t, requesterTest, responderTest)\n\t\tt.Logf(\"\\tduration: %s\", time.Since(start))\n\t}\n\n\tt.Log(\"Responder signs invalid proof\")\n\t{\n\t\tstart := time.Now()\n\n\t\tvar requesterTest requesterTestFunc = func(\n\t\t\tt *testing.T,\n\t\t\tstream p2pnetwork.Stream,\n\t\t\tmh *mockedHandshake,\n\t\t) {\n\t\t\tdefer ipfsutil.FullClose(stream)\n\n\t\t\terr := Request(\n\t\t\t\tstream,\n\t\t\t\tmh.requester.accountID,\n\t\t\t\tmh.responder.accountID.GetPublic(),\n\t\t\t)\n\t\t\trequire.Equal(t, errcode.Codes(err), []errcode.ErrCode{errcode.ErrCode_ErrHandshakeResponderAccept, errcode.ErrCode_ErrCryptoSignatureVerification})\n\t\t}\n\n\t\tvar responderTest responderTestFunc = func(\n\t\t\tt *testing.T,\n\t\t\tstream p2pnetwork.Stream,\n\t\t\tmh *mockedHandshake,\n\t\t\twg *sync.WaitGroup,\n\t\t) {\n\t\t\tvar response ResponderAcceptPayload\n\n\t\t\tdefer wg.Done()\n\n\t\t\thc := newTestHandshakeContext(stream, mh.responder.accountID, nil)\n\n\t\t\terr := hc.receiveRequesterHello()\n\t\t\trequire.NoError(t, err, \"receive RequesterHello failed\")\n\n\t\t\terr = hc.sendResponderHello()\n\t\t\trequire.NoError(t, err, \"send ResponderHello failed\")\n\n\t\t\terr = hc.receiveRequesterAuthenticate()\n\t\t\trequire.NoError(t, err, \"receive RequesterAuthenticate failed\")\n\n\t\t\t// Sign invalid proof\n\t\t\tresponse.ResponderAccountSig, err = hc.ownAccountID.Sign([]byte(\"WrongProof\"))\n\t\t\trequire.NoError(t, err, \"sharedEphemeral signing failed\")\n\n\t\t\tresponseBytes, err := proto.Marshal(&response)\n\t\t\trequire.NoError(t, err, \"response marshaling failed\")\n\n\t\t\tboxKey, err := hc.computeResponderAcceptBoxKey()\n\t\t\trequire.NoError(t, err, \"ResponderAccept Accept box key gen failed\")\n\n\t\t\tboxContent := box.SealAfterPrecomputation(\n\t\t\t\tnil,\n\t\t\t\tresponseBytes,\n\t\t\t\t&nonceResponderAccept,\n\t\t\t\tboxKey,\n\t\t\t)\n\n\t\t\thc.writer.WriteMsg(&BoxEnvelope{Box: boxContent})\n\n\t\t\tipfsutil.FullClose(stream)\n\t\t}\n\n\t\trunHandshakeTest(t, requesterTest, responderTest)\n\t\tt.Logf(\"\\tduration: %s\", time.Since(start))\n\t}\n\n\tt.Log(\"Responder sends invalid response content\")\n\t{\n\t\tstart := time.Now()\n\n\t\tvar requesterTest requesterTestFunc = func(\n\t\t\tt *testing.T,\n\t\t\tstream p2pnetwork.Stream,\n\t\t\tmh *mockedHandshake,\n\t\t) {\n\t\t\tdefer ipfsutil.FullClose(stream)\n\n\t\t\terr := Request(\n\t\t\t\tstream,\n\t\t\t\tmh.requester.accountID,\n\t\t\t\tmh.responder.accountID.GetPublic(),\n\t\t\t)\n\t\t\trequire.Equal(t, errcode.Codes(err), []errcode.ErrCode{errcode.ErrCode_ErrHandshakeResponderAccept, errcode.ErrCode_ErrDeserialization})\n\t\t}\n\n\t\tvar responderTest responderTestFunc = func(\n\t\t\tt *testing.T,\n\t\t\tstream p2pnetwork.Stream,\n\t\t\tmh *mockedHandshake,\n\t\t\twg *sync.WaitGroup,\n\t\t) {\n\t\t\tdefer wg.Done()\n\n\t\t\thc := newTestHandshakeContext(stream, mh.responder.accountID, nil)\n\n\t\t\terr := hc.receiveRequesterHello()\n\t\t\trequire.NoError(t, err, \"receive RequesterHello failed\")\n\n\t\t\terr = hc.sendResponderHello()\n\t\t\trequire.NoError(t, err, \"send ResponderHello failed\")\n\n\t\t\terr = hc.receiveRequesterAuthenticate()\n\t\t\trequire.NoError(t, err, \"receive RequesterAuthenticate failed\")\n\n\t\t\t// Send invalid response content\n\t\t\tresponseBytes := []byte(\"WrongResponseContent\")\n\n\t\t\tboxKey, err := hc.computeResponderAcceptBoxKey()\n\t\t\trequire.NoError(t, err, \"ResponderAccept Accept box key gen failed\")\n\n\t\t\tboxContent := box.SealAfterPrecomputation(\n\t\t\t\tnil,\n\t\t\t\tresponseBytes,\n\t\t\t\t&nonceResponderAccept,\n\t\t\t\tboxKey,\n\t\t\t)\n\n\t\t\thc.writer.WriteMsg(&BoxEnvelope{Box: boxContent})\n\n\t\t\tipfsutil.FullClose(stream)\n\t\t}\n\n\t\trunHandshakeTest(t, requesterTest, responderTest)\n\t\tt.Logf(\"\\tduration: %s\", time.Since(start))\n\t}\n\n\tt.Log(\"Responder seals box using another key\")\n\t{\n\t\tstart := time.Now()\n\n\t\tvar requesterTest requesterTestFunc = func(\n\t\t\tt *testing.T,\n\t\t\tstream p2pnetwork.Stream,\n\t\t\tmh *mockedHandshake,\n\t\t) {\n\t\t\tdefer ipfsutil.FullClose(stream)\n\n\t\t\terr := Request(\n\t\t\t\tstream,\n\t\t\t\tmh.requester.accountID,\n\t\t\t\tmh.responder.accountID.GetPublic(),\n\t\t\t)\n\t\t\trequire.Equal(t, errcode.Codes(err), []errcode.ErrCode{errcode.ErrCode_ErrHandshakeResponderAccept, errcode.ErrCode_ErrCryptoDecrypt})\n\t\t}\n\n\t\tvar responderTest responderTestFunc = func(\n\t\t\tt *testing.T,\n\t\t\tstream p2pnetwork.Stream,\n\t\t\tmh *mockedHandshake,\n\t\t\twg *sync.WaitGroup,\n\t\t) {\n\t\t\tvar response ResponderAcceptPayload\n\n\t\t\tdefer wg.Done()\n\n\t\t\thc := newTestHandshakeContext(stream, mh.responder.accountID, nil)\n\n\t\t\terr := hc.receiveRequesterHello()\n\t\t\trequire.NoError(t, err, \"receive RequesterHello failed\")\n\n\t\t\terr = hc.sendResponderHello()\n\t\t\trequire.NoError(t, err, \"send ResponderHello failed\")\n\n\t\t\terr = hc.receiveRequesterAuthenticate()\n\t\t\trequire.NoError(t, err, \"receive RequesterAuthenticate failed\")\n\n\t\t\tresponse.ResponderAccountSig, err = hc.ownAccountID.Sign(hc.sharedEphemeral[:])\n\t\t\trequire.NoError(t, err, \"sharedEphemeral signing failed\")\n\n\t\t\tresponseBytes, err := proto.Marshal(&response)\n\t\t\trequire.NoError(t, err, \"response marshaling failed\")\n\n\t\t\t// Seal box using another key\n\t\t\twrongBoxKey := &[32]byte{}\n\t\t\tcrand.Read(wrongBoxKey[:])\n\n\t\t\tboxContent := box.SealAfterPrecomputation(\n\t\t\t\tnil,\n\t\t\t\tresponseBytes,\n\t\t\t\t&nonceResponderAccept,\n\t\t\t\twrongBoxKey,\n\t\t\t)\n\n\t\t\thc.writer.WriteMsg(&BoxEnvelope{Box: boxContent})\n\n\t\t\tipfsutil.FullClose(stream)\n\t\t}\n\n\t\trunHandshakeTest(t, requesterTest, responderTest)\n\t\tt.Logf(\"\\tduration: %s\", time.Since(start))\n\t}\n\n\tt.Log(\"Responder seals using another nonce\")\n\t{\n\t\tstart := time.Now()\n\n\t\tvar requesterTest requesterTestFunc = func(\n\t\t\tt *testing.T,\n\t\t\tstream p2pnetwork.Stream,\n\t\t\tmh *mockedHandshake,\n\t\t) {\n\t\t\tdefer ipfsutil.FullClose(stream)\n\n\t\t\terr := Request(\n\t\t\t\tstream,\n\t\t\t\tmh.requester.accountID,\n\t\t\t\tmh.responder.accountID.GetPublic(),\n\t\t\t)\n\t\t\trequire.Equal(t, errcode.Codes(err), []errcode.ErrCode{errcode.ErrCode_ErrHandshakeResponderAccept, errcode.ErrCode_ErrCryptoDecrypt})\n\t\t}\n\n\t\tvar responderTest responderTestFunc = func(\n\t\t\tt *testing.T,\n\t\t\tstream p2pnetwork.Stream,\n\t\t\tmh *mockedHandshake,\n\t\t\twg *sync.WaitGroup,\n\t\t) {\n\t\t\tvar response ResponderAcceptPayload\n\n\t\t\tdefer wg.Done()\n\n\t\t\thc := newTestHandshakeContext(stream, mh.responder.accountID, nil)\n\n\t\t\terr := hc.receiveRequesterHello()\n\t\t\trequire.NoError(t, err, \"receive RequesterHello failed\")\n\n\t\t\terr = hc.sendResponderHello()\n\t\t\trequire.NoError(t, err, \"send ResponderHello failed\")\n\n\t\t\terr = hc.receiveRequesterAuthenticate()\n\t\t\trequire.NoError(t, err, \"receive RequesterAuthenticate failed\")\n\n\t\t\tresponse.ResponderAccountSig, err = hc.ownAccountID.Sign(hc.sharedEphemeral[:])\n\t\t\trequire.NoError(t, err, \"sharedEphemeral signing failed\")\n\n\t\t\tresponseBytes, err := proto.Marshal(&response)\n\t\t\trequire.NoError(t, err, \"response marshaling failed\")\n\n\t\t\tboxKey, err := hc.computeResponderAcceptBoxKey()\n\t\t\trequire.NoError(t, err, \"ResponderAccept Accept box key gen failed\")\n\n\t\t\t// Seals using another nonce\n\t\t\twrongNonce, err := cryptoutil.GenerateNonce()\n\t\t\trequire.NoError(t, err, \"nonce generation failed\")\n\n\t\t\tboxContent := box.SealAfterPrecomputation(\n\t\t\t\tnil,\n\t\t\t\tresponseBytes,\n\t\t\t\twrongNonce,\n\t\t\t\tboxKey,\n\t\t\t)\n\n\t\t\thc.writer.WriteMsg(&BoxEnvelope{Box: boxContent})\n\n\t\t\tipfsutil.FullClose(stream)\n\t\t}\n\n\t\trunHandshakeTest(t, requesterTest, responderTest)\n\t\tt.Logf(\"\\tduration: %s\", time.Since(start))\n\t}\n\n\tt.Log(\"Responder sends invalid box content\")\n\t{\n\t\tstart := time.Now()\n\n\t\tvar requesterTest requesterTestFunc = func(\n\t\t\tt *testing.T,\n\t\t\tstream p2pnetwork.Stream,\n\t\t\tmh *mockedHandshake,\n\t\t) {\n\t\t\tdefer ipfsutil.FullClose(stream)\n\n\t\t\terr := Request(\n\t\t\t\tstream,\n\t\t\t\tmh.requester.accountID,\n\t\t\t\tmh.responder.accountID.GetPublic(),\n\t\t\t)\n\t\t\trequire.Equal(t, errcode.Codes(err), []errcode.ErrCode{errcode.ErrCode_ErrHandshakeResponderAccept, errcode.ErrCode_ErrCryptoDecrypt})\n\t\t}\n\n\t\tvar responderTest responderTestFunc = func(\n\t\t\tt *testing.T,\n\t\t\tstream p2pnetwork.Stream,\n\t\t\tmh *mockedHandshake,\n\t\t\twg *sync.WaitGroup,\n\t\t) {\n\t\t\tdefer wg.Done()\n\n\t\t\thc := newTestHandshakeContext(stream, mh.responder.accountID, nil)\n\n\t\t\terr := hc.receiveRequesterHello()\n\t\t\trequire.NoError(t, err, \"receive RequesterHello failed\")\n\n\t\t\terr = hc.sendResponderHello()\n\t\t\trequire.NoError(t, err, \"send ResponderHello failed\")\n\n\t\t\terr = hc.receiveRequesterAuthenticate()\n\t\t\trequire.NoError(t, err, \"receive RequesterAuthenticate failed\")\n\n\t\t\t// Send wrong boxContent\n\t\t\thc.writer.WriteMsg(&BoxEnvelope{Box: []byte(\"WrongBoxContent\")})\n\n\t\t\tipfsutil.FullClose(stream)\n\t\t}\n\n\t\trunHandshakeTest(t, requesterTest, responderTest)\n\t\tt.Logf(\"\\tduration: %s\", time.Since(start))\n\t}\n}\n\nfunc TestInvalidResponderAcceptAck(t *testing.T) {\n\ttestutil.FilterSpeed(t, testutil.Slow)\n\n\tt.Log(\"Requester interrupts by closing stream\")\n\t{\n\t\tstart := time.Now()\n\n\t\tvar requesterTest requesterTestFunc = func(\n\t\t\tt *testing.T,\n\t\t\tstream p2pnetwork.Stream,\n\t\t\tmh *mockedHandshake,\n\t\t) {\n\t\t\thc := newTestHandshakeContext(\n\t\t\t\tstream,\n\t\t\t\tmh.requester.accountID,\n\t\t\t\tmh.responder.accountID.GetPublic(),\n\t\t\t)\n\n\t\t\terr := hc.sendRequesterHello()\n\t\t\trequire.NoError(t, err, \"send RequesterHello failed\")\n\n\t\t\terr = hc.receiveResponderHello()\n\t\t\trequire.NoError(t, err, \"receive ResponderHello failed\")\n\n\t\t\terr = hc.sendRequesterAuthenticate()\n\t\t\trequire.NoError(t, err, \"send RequesterAuthenticate failed\")\n\n\t\t\terr = hc.receiveResponderAccept()\n\t\t\trequire.NoError(t, err, \"receive ResponderAccept failed\")\n\n\t\t\tipfsutil.FullClose(stream)\n\t\t}\n\n\t\tvar responderTest responderTestFunc = func(\n\t\t\tt *testing.T,\n\t\t\tstream p2pnetwork.Stream,\n\t\t\tmh *mockedHandshake,\n\t\t\twg *sync.WaitGroup,\n\t\t) {\n\t\t\tdefer wg.Done()\n\t\t\tdefer ipfsutil.FullClose(stream)\n\n\t\t\t_, err := Response(stream, mh.responder.accountID)\n\t\t\trequire.Equal(t, errcode.Codes(err), []errcode.ErrCode{errcode.ErrCode_ErrHandshakeRequesterAcknowledge, errcode.ErrCode_ErrStreamRead})\n\t\t}\n\n\t\trunHandshakeTest(t, requesterTest, responderTest)\n\t\tt.Logf(\"\\tduration: %s\", time.Since(start))\n\t}\n\n\tt.Log(\"Requester sends acknowledge with: success == false\")\n\t{\n\t\tstart := time.Now()\n\n\t\tvar requesterTest requesterTestFunc = func(\n\t\t\tt *testing.T,\n\t\t\tstream p2pnetwork.Stream,\n\t\t\tmh *mockedHandshake,\n\t\t) {\n\t\t\thc := newTestHandshakeContext(\n\t\t\t\tstream,\n\t\t\t\tmh.requester.accountID,\n\t\t\t\tmh.responder.accountID.GetPublic(),\n\t\t\t)\n\n\t\t\terr := hc.sendRequesterHello()\n\t\t\trequire.NoError(t, err, \"send RequesterHello failed\")\n\n\t\t\terr = hc.receiveResponderHello()\n\t\t\trequire.NoError(t, err, \"receive ResponderHello failed\")\n\n\t\t\terr = hc.sendRequesterAuthenticate()\n\t\t\trequire.NoError(t, err, \"send RequesterAuthenticate failed\")\n\n\t\t\terr = hc.receiveResponderAccept()\n\t\t\trequire.NoError(t, err, \"receive ResponderAccept failed\")\n\n\t\t\tacknowledge := &RequesterAcknowledgePayload{Success: false}\n\n\t\t\thc.writer.WriteMsg(acknowledge)\n\n\t\t\tipfsutil.FullClose(stream)\n\t\t}\n\n\t\tvar responderTest responderTestFunc = func(\n\t\t\tt *testing.T,\n\t\t\tstream p2pnetwork.Stream,\n\t\t\tmh *mockedHandshake,\n\t\t\twg *sync.WaitGroup,\n\t\t) {\n\t\t\tdefer wg.Done()\n\t\t\tdefer ipfsutil.FullClose(stream)\n\n\t\t\t_, err := Response(stream, mh.responder.accountID)\n\t\t\trequire.Equal(t, errcode.Codes(err), []errcode.ErrCode{errcode.ErrCode_ErrHandshakeRequesterAcknowledge, errcode.ErrCode_ErrInvalidInput})\n\t\t}\n\n\t\trunHandshakeTest(t, requesterTest, responderTest)\n\t\tt.Logf(\"\\tduration: %s\", time.Since(start))\n\t}\n}\n"
  },
  {
    "path": "internal/handshake/handshake_util_test.go",
    "content": "package handshake\n\nimport (\n\t\"context\"\n\tcrand \"crypto/rand\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\tp2pcrypto \"github.com/libp2p/go-libp2p/core/crypto\"\n\tp2pnetwork \"github.com/libp2p/go-libp2p/core/network\"\n\tp2ppeer \"github.com/libp2p/go-libp2p/core/peer\"\n\tmocknet \"github.com/libp2p/go-libp2p/p2p/net/mock\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"berty.tech/weshnet/v2/pkg/ipfsutil\"\n\t\"berty.tech/weshnet/v2/pkg/protoio\"\n\t\"berty.tech/weshnet/v2/pkg/tinder\"\n)\n\nconst testProtocolID = \"/berty/handshake_test/1.0.0\"\n\ntype mockedPeer struct {\n\taccountID p2pcrypto.PrivKey\n\tcoreAPI   ipfsutil.CoreAPIMock\n\tpeerInfo  p2ppeer.AddrInfo\n}\n\ntype mockedHandshake struct {\n\trequester *mockedPeer\n\tresponder *mockedPeer\n}\n\ntype requesterTestFunc func(\n\tt *testing.T,\n\tstream p2pnetwork.Stream,\n\tmh *mockedHandshake,\n)\n\ntype responderTestFunc func(\n\tt *testing.T,\n\tstream p2pnetwork.Stream,\n\tmh *mockedHandshake,\n\twg *sync.WaitGroup,\n)\n\nfunc newMockedPeer(t *testing.T, ctx context.Context, ipfsOpts *ipfsutil.TestingAPIOpts) *mockedPeer {\n\taccountID, _, err := p2pcrypto.GenerateEd25519Key(crand.Reader)\n\trequire.NoError(t, err, \"can't create new identity\")\n\n\tcoreAPI := ipfsutil.TestingCoreAPIUsingMockNet(ctx, t, ipfsOpts)\n\tpeerInfo := coreAPI.MockNode().Peerstore.PeerInfo(coreAPI.MockNode().Identity)\n\n\treturn &mockedPeer{\n\t\taccountID: accountID,\n\t\tcoreAPI:   coreAPI,\n\t\tpeerInfo:  peerInfo,\n\t}\n}\n\nfunc newMockedHandshake(t *testing.T, ctx context.Context) *mockedHandshake {\n\tt.Helper()\n\n\tmn := mocknet.New()\n\tt.Cleanup(func() { mn.Close() })\n\n\topts := &ipfsutil.TestingAPIOpts{\n\t\tMocknet:         mn,\n\t\tDiscoveryServer: tinder.NewMockDriverServer(),\n\t}\n\trequester := newMockedPeer(t, ctx, opts)\n\tresponder := newMockedPeer(t, ctx, opts)\n\n\t// link responder & requester\n\terr := opts.Mocknet.LinkAll()\n\trequire.NoError(t, err, \"can't link peers\")\n\n\t// connect responder & requester\n\terr = opts.Mocknet.ConnectAllButSelf()\n\trequire.NoError(t, err, \"can't connect peers\")\n\n\treturn &mockedHandshake{\n\t\trequester: requester,\n\t\tresponder: responder,\n\t}\n}\n\nfunc (mh *mockedHandshake) close(t *testing.T) {\n\tt.Helper()\n\n\tmh.requester.coreAPI.Close()\n\tmh.responder.coreAPI.Close()\n}\n\nfunc newTestHandshakeContext(stream p2pnetwork.Stream, ownAccountID p2pcrypto.PrivKey, peerAccountID p2pcrypto.PubKey) *handshakeContext {\n\treturn &handshakeContext{\n\t\treader:          protoio.NewDelimitedReader(stream, 2048),\n\t\twriter:          protoio.NewDelimitedWriter(stream),\n\t\townAccountID:    ownAccountID,\n\t\tpeerAccountID:   peerAccountID,\n\t\tsharedEphemeral: &[32]byte{},\n\t}\n}\n\nfunc runHandshakeTest(t *testing.T, requesterTest requesterTestFunc, responderTest responderTestFunc) {\n\tvar wg sync.WaitGroup\n\n\tctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)\n\tdefer cancel()\n\n\tmh := newMockedHandshake(t, ctx)\n\tdefer mh.close(t)\n\n\tmh.responder.coreAPI.MockNode().PeerHost.SetStreamHandler(\n\t\ttestProtocolID,\n\t\tfunc(stream p2pnetwork.Stream) {\n\t\t\twg.Add(1)\n\t\t\tresponderTest(t, stream, mh, &wg)\n\t\t},\n\t)\n\n\tstream, err := mh.requester.coreAPI.MockNode().PeerHost.NewStream(\n\t\tctx,\n\t\tmh.responder.peerInfo.ID,\n\t\ttestProtocolID,\n\t)\n\trequire.NoError(t, err, \"requester can't dial responder\")\n\trequesterTest(t, stream, mh)\n\n\twg.Wait()\n}\n"
  },
  {
    "path": "internal/handshake/request.go",
    "content": "package handshake\n\nimport (\n\t\"context\"\n\t\"errors\"\n\n\tp2pcrypto \"github.com/libp2p/go-libp2p/core/crypto\"\n\t\"go.uber.org/zap\"\n\t\"golang.org/x/crypto/nacl/box\"\n\t\"google.golang.org/protobuf/proto\"\n\n\t\"berty.tech/weshnet/v2/pkg/cryptoutil\"\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n\t\"berty.tech/weshnet/v2/pkg/protoio\"\n\t\"berty.tech/weshnet/v2/pkg/tyber\"\n)\n\n// RequestUsingReaderWriter init a handshake with the responder, using provided io reader and writer\nfunc RequestUsingReaderWriter(ctx context.Context, logger *zap.Logger, reader protoio.Reader, writer protoio.Writer, ownAccountID p2pcrypto.PrivKey, peerAccountID p2pcrypto.PubKey) error {\n\thc := &handshakeContext{\n\t\treader:          reader,\n\t\twriter:          writer,\n\t\townAccountID:    ownAccountID,\n\t\tpeerAccountID:   peerAccountID,\n\t\tsharedEphemeral: &[cryptoutil.KeySize]byte{},\n\t}\n\n\t// Handshake steps on requester side (see comments below)\n\tif err := hc.sendRequesterHello(); err != nil {\n\t\treturn errcode.ErrCode_ErrHandshakeRequesterHello.Wrap(err)\n\t}\n\ttyber.LogStep(ctx, logger, \"Sent hello\", hc.toTyberStepMutator())\n\tif err := hc.receiveResponderHello(); err != nil {\n\t\treturn errcode.ErrCode_ErrHandshakeResponderHello.Wrap(err)\n\t}\n\ttyber.LogStep(ctx, logger, \"Received hello\", hc.toTyberStepMutator())\n\tif err := hc.sendRequesterAuthenticate(); err != nil {\n\t\treturn errcode.ErrCode_ErrHandshakeRequesterAuthenticate.Wrap(err)\n\t}\n\ttyber.LogStep(ctx, logger, \"Sent authenticate\", hc.toTyberStepMutator())\n\tif err := hc.receiveResponderAccept(); err != nil {\n\t\treturn errcode.ErrCode_ErrHandshakeResponderAccept.Wrap(err)\n\t}\n\ttyber.LogStep(ctx, logger, \"Received accept\", hc.toTyberStepMutator())\n\tif err := hc.sendRequesterAcknowledge(); err != nil {\n\t\treturn errcode.ErrCode_ErrHandshakeRequesterAcknowledge.Wrap(err)\n\t}\n\ttyber.LogStep(ctx, logger, \"Sent acknowledge\", hc.toTyberStepMutator())\n\n\treturn nil\n}\n\n// 1st step - Requester sends: a\nfunc (hc *handshakeContext) sendRequesterHello() error {\n\tif err := hc.generateOwnEphemeralAndSendPubKey(); err != nil {\n\t\treturn errcode.ErrCode_ErrHandshakeOwnEphemeralKeyGenSend.Wrap(err)\n\t}\n\n\treturn nil\n}\n\n// 2nd step - Requester receives: b\nfunc (hc *handshakeContext) receiveResponderHello() error {\n\tif err := hc.receivePeerEphemeralPubKey(); err != nil {\n\t\treturn errcode.ErrCode_ErrHandshakePeerEphemeralKeyRecv.Wrap(err)\n\t}\n\n\t// Compute shared key from Ephemeral keys\n\tbox.Precompute(hc.sharedEphemeral, hc.peerEphemeral, hc.ownEphemeral)\n\n\treturn nil\n}\n\n// 3rd step - Requester sends: box[a.b|a.B](A,sig[A](a.b))\nfunc (hc *handshakeContext) sendRequesterAuthenticate() error {\n\tvar (\n\t\trequest RequesterAuthenticatePayload\n\t\terr     error\n\t)\n\n\t// Set own AccountID pub key and proof (shared_a_b signed by own AccountID)\n\t// in RequesterAuthenticatePayload message before marshaling it\n\trequest.RequesterAccountId, err = p2pcrypto.MarshalPublicKey(hc.ownAccountID.GetPublic())\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\trequest.RequesterAccountSig, err = hc.ownAccountID.Sign(hc.sharedEphemeral[:])\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrCryptoSignature.Wrap(err)\n\t}\n\trequestBytes, err := proto.Marshal(&request)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\t// Compute box key and seal marshaled RequesterAuthenticatePayload using\n\t// constant nonce (see handshake.go)\n\tboxKey, err := hc.computeRequesterAuthenticateBoxKey(true)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrHandshakeRequesterAuthenticateBoxKeyGen.Wrap(err)\n\t}\n\tboxContent := box.SealAfterPrecomputation(\n\t\tnil,\n\t\trequestBytes,\n\t\t&nonceRequesterAuthenticate,\n\t\tboxKey,\n\t)\n\n\t// Send BoxEnvelope to responder\n\tif err = hc.writer.WriteMsg(&BoxEnvelope{Box: boxContent}); err != nil {\n\t\treturn errcode.ErrCode_ErrStreamWrite.Wrap(err)\n\t}\n\n\treturn nil\n}\n\n// 4th step - Requester receives: box[a.b|A.B](sig[B](a.b))\nfunc (hc *handshakeContext) receiveResponderAccept() error {\n\tvar (\n\t\tboxEnvelope BoxEnvelope\n\t\tresponse    ResponderAcceptPayload\n\t)\n\n\t// Receive BoxEnvelope from responder\n\tif err := hc.reader.ReadMsg(&boxEnvelope); err != nil {\n\t\treturn errcode.ErrCode_ErrStreamRead.Wrap(err)\n\t}\n\n\t// Compute box key and open marshaled RequesterAuthenticatePayload using\n\t// constant nonce (see handshake.go)\n\tboxKey, err := hc.computeResponderAcceptBoxKey()\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrHandshakeResponderAcceptBoxKeyGen.Wrap(err)\n\t}\n\n\trespBytes, _ := box.OpenAfterPrecomputation(\n\t\tnil,\n\t\tboxEnvelope.Box,\n\t\t&nonceResponderAccept,\n\t\tboxKey,\n\t)\n\tif respBytes == nil {\n\t\terr := errors.New(\"box opening failed\")\n\t\treturn errcode.ErrCode_ErrCryptoDecrypt.Wrap(err)\n\t}\n\n\t// Unmarshal ResponderAcceptPayload\n\n\terr = proto.Unmarshal(respBytes, &response)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\t// Verify proof (shared_a_b signed by peer's AccountID)\n\tvalid, err := hc.peerAccountID.Verify(\n\t\thc.sharedEphemeral[:],\n\t\tresponse.ResponderAccountSig,\n\t)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrCryptoSignatureVerification.Wrap(err)\n\t} else if !valid {\n\t\treturn errcode.ErrCode_ErrCryptoSignatureVerification\n\t}\n\n\treturn nil\n}\n\n// 5th step - Requester sends: ok\nfunc (hc *handshakeContext) sendRequesterAcknowledge() error {\n\tacknowledge := &RequesterAcknowledgePayload{Success: true}\n\n\t// Send Acknowledge to responder\n\tif err := hc.writer.WriteMsg(acknowledge); err != nil {\n\t\treturn errcode.ErrCode_ErrStreamWrite.Wrap(err)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "internal/handshake/response.go",
    "content": "package handshake\n\nimport (\n\t\"context\"\n\t\"errors\"\n\n\tp2pcrypto \"github.com/libp2p/go-libp2p/core/crypto\"\n\t\"go.uber.org/zap\"\n\t\"golang.org/x/crypto/nacl/box\"\n\t\"google.golang.org/protobuf/proto\"\n\n\t\"berty.tech/weshnet/v2/pkg/cryptoutil\"\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n\t\"berty.tech/weshnet/v2/pkg/protoio\"\n\t\"berty.tech/weshnet/v2/pkg/tyber\"\n)\n\n// ResponseUsingReaderWriter handle the handshake inited by the requester, using provided io reader and writer\nfunc ResponseUsingReaderWriter(ctx context.Context, logger *zap.Logger, reader protoio.Reader, writer protoio.Writer, ownAccountID p2pcrypto.PrivKey) (p2pcrypto.PubKey, error) {\n\thc := &handshakeContext{\n\t\treader:          reader,\n\t\twriter:          writer,\n\t\townAccountID:    ownAccountID,\n\t\tsharedEphemeral: &[cryptoutil.KeySize]byte{},\n\t}\n\n\t// Handshake steps on responder side (see comments below)\n\tif err := hc.receiveRequesterHello(); err != nil {\n\t\treturn nil, errcode.ErrCode_ErrHandshakeRequesterHello.Wrap(err)\n\t}\n\ttyber.LogStep(ctx, logger, \"Received hello\", hc.toTyberStepMutator(), tyber.ForceReopen)\n\tif err := hc.sendResponderHello(); err != nil {\n\t\treturn nil, errcode.ErrCode_ErrHandshakeResponderHello.Wrap(err)\n\t}\n\ttyber.LogStep(ctx, logger, \"Sent hello\", hc.toTyberStepMutator(), tyber.ForceReopen)\n\tif err := hc.receiveRequesterAuthenticate(); err != nil {\n\t\treturn nil, errcode.ErrCode_ErrHandshakeRequesterAuthenticate.Wrap(err)\n\t}\n\ttyber.LogStep(ctx, logger, \"Received authenticate\", hc.toTyberStepMutator(), tyber.ForceReopen)\n\tif err := hc.sendResponderAccept(); err != nil {\n\t\treturn nil, errcode.ErrCode_ErrHandshakeResponderAccept.Wrap(err)\n\t}\n\ttyber.LogStep(ctx, logger, \"Sent accept\", hc.toTyberStepMutator(), tyber.ForceReopen)\n\tif err := hc.receiveRequesterAcknowledge(); err != nil {\n\t\treturn nil, errcode.ErrCode_ErrHandshakeRequesterAcknowledge.Wrap(err)\n\t}\n\ttyber.LogStep(ctx, logger, \"Received acknowledge\", hc.toTyberStepMutator(), tyber.ForceReopen)\n\n\treturn hc.peerAccountID, nil\n}\n\n// 1st step - Responder receives: a\nfunc (hc *handshakeContext) receiveRequesterHello() error {\n\tif err := hc.receivePeerEphemeralPubKey(); err != nil {\n\t\treturn errcode.ErrCode_ErrHandshakePeerEphemeralKeyRecv.Wrap(err)\n\t}\n\n\treturn nil\n}\n\n// 2nd step - Responder sends: b\nfunc (hc *handshakeContext) sendResponderHello() error {\n\tif err := hc.generateOwnEphemeralAndSendPubKey(); err != nil {\n\t\treturn errcode.ErrCode_ErrHandshakeOwnEphemeralKeyGenSend.Wrap(err)\n\t}\n\n\t// Compute shared key from Ephemeral keys\n\tbox.Precompute(hc.sharedEphemeral, hc.peerEphemeral, hc.ownEphemeral)\n\n\treturn nil\n}\n\n// 3rd step - Responder receives: box[a.b|a.B](A,sig[A](a.b))\nfunc (hc *handshakeContext) receiveRequesterAuthenticate() error {\n\tvar (\n\t\tboxEnvelope BoxEnvelope\n\t\trequest     RequesterAuthenticatePayload\n\t)\n\n\t// Receive BoxEnvelope from requester\n\tif err := hc.reader.ReadMsg(&boxEnvelope); err != nil {\n\t\treturn errcode.ErrCode_ErrStreamRead.Wrap(err)\n\t}\n\n\t// Compute box key and open marshaled RequesterAuthenticatePayload using\n\t// constant nonce (see handshake.go)\n\tboxKey, err := hc.computeRequesterAuthenticateBoxKey(false)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrHandshakeRequesterAuthenticateBoxKeyGen.Wrap(err)\n\t}\n\trequestBytes, _ := box.OpenAfterPrecomputation(\n\t\tnil,\n\t\tboxEnvelope.Box,\n\t\t&nonceRequesterAuthenticate,\n\t\tboxKey,\n\t)\n\tif requestBytes == nil {\n\t\terr := errors.New(\"box opening failed\")\n\t\treturn errcode.ErrCode_ErrCryptoDecrypt.Wrap(err)\n\t}\n\n\t// Unmarshal RequesterAuthenticatePayload and RequesterAccountId\n\terr = proto.Unmarshal(requestBytes, &request)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\thc.peerAccountID, err = p2pcrypto.UnmarshalPublicKey(request.RequesterAccountId)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\t// Verify proof (shared_a_b signed by peer's AccountID)\n\tvalid, err := hc.peerAccountID.Verify(\n\t\thc.sharedEphemeral[:],\n\t\trequest.RequesterAccountSig,\n\t)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrCryptoSignatureVerification.Wrap(err)\n\t} else if !valid {\n\t\treturn errcode.ErrCode_ErrCryptoSignatureVerification\n\t}\n\n\treturn nil\n}\n\n// 4th step - Responder sends: box[a.b|A.B](sig[B](a.b))\nfunc (hc *handshakeContext) sendResponderAccept() error {\n\tvar (\n\t\tresponse ResponderAcceptPayload\n\t\terr      error\n\t)\n\n\t// Set proof (shared_a_b signed by own AccountID) in ResponderAcceptPayload\n\t// before marshaling it\n\tresponse.ResponderAccountSig, err = hc.ownAccountID.Sign(hc.sharedEphemeral[:])\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrCryptoSignature.Wrap(err)\n\t}\n\tresponseBytes, err := proto.Marshal(&response)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\t// Compute box key and seal marshaled ResponderAcceptPayload using\n\t// constant nonce (see handshake.go)\n\tboxKey, err := hc.computeResponderAcceptBoxKey()\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrHandshakeResponderAcceptBoxKeyGen.Wrap(err)\n\t}\n\tboxContent := box.SealAfterPrecomputation(\n\t\tnil,\n\t\tresponseBytes,\n\t\t&nonceResponderAccept,\n\t\tboxKey,\n\t)\n\n\t// Send BoxEnvelope to requester\n\tif err = hc.writer.WriteMsg(&BoxEnvelope{Box: boxContent}); err != nil {\n\t\treturn errcode.ErrCode_ErrStreamWrite.Wrap(err)\n\t}\n\n\treturn nil\n}\n\n// 5th step - Responder receives: ok\nfunc (hc *handshakeContext) receiveRequesterAcknowledge() error {\n\tvar acknowledge RequesterAcknowledgePayload\n\n\t// Receive Acknowledge from requester\n\tif err := hc.reader.ReadMsg(&acknowledge); err != nil {\n\t\treturn errcode.ErrCode_ErrStreamRead.Wrap(err)\n\t}\n\n\tif !acknowledge.Success {\n\t\treturn errcode.ErrCode_ErrInvalidInput\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "internal/notify/notify.go",
    "content": "package notify\n\nimport (\n\t\"context\"\n\t\"sync\"\n)\n\ntype Notify struct {\n\tL sync.Locker\n\n\tcc chan struct{}\n\tmu sync.Mutex\n}\n\nfunc New(l sync.Locker) *Notify {\n\treturn &Notify{L: l}\n}\n\nfunc (n *Notify) getChan() <-chan struct{} {\n\tn.mu.Lock()\n\tdefer n.mu.Unlock()\n\n\tif n.cc == nil {\n\t\tn.cc = make(chan struct{})\n\t}\n\treturn n.cc\n}\n\nfunc (n *Notify) Wait(ctx context.Context) (ok bool) {\n\tsignal := n.getChan()\n\n\tn.L.Unlock()\n\tselect {\n\tcase <-ctx.Done():\n\t\tok = false\n\tcase <-signal:\n\t\tok = true\n\t}\n\tn.L.Lock()\n\n\treturn\n}\n\nfunc (n *Notify) Broadcast() {\n\tn.mu.Lock()\n\tif n.cc != nil {\n\t\tclose(n.cc)\n\t\tn.cc = nil\n\t}\n\tn.mu.Unlock()\n}\n"
  },
  {
    "path": "internal/notify/notify_test.go",
    "content": "package notify\n\nimport (\n\t\"context\"\n\t\"sync\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestNotify(t *testing.T) {\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\tlocker := sync.Mutex{}\n\tntfy := New(&locker)\n\n\tlocker.Lock()\n\n\tvar ok bool = false\n\tgo func() {\n\t\tlocker.Lock()\n\t\tassert.False(t, ok)\n\t\tok = true\n\t\tntfy.Broadcast()\n\t\tlocker.Unlock()\n\t}()\n\n\tctxok := ntfy.Wait(ctx)\n\tassert.True(t, ctxok)\n\tassert.True(t, ok)\n\n\tlocker.Unlock()\n}\n\nfunc TestNotifyConcurrentWait(t *testing.T) {\n\tconst n = 200\n\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\tlocker := sync.Mutex{}\n\tnotify := New(&locker)\n\n\trunning := make(chan int, n)\n\tawake := make(chan int, n)\n\n\tfor i := 0; i < n; i++ {\n\t\tgo func(g int) {\n\t\t\tlocker.Lock()\n\t\t\tok := true\n\t\t\tfor ok {\n\t\t\t\trunning <- g\n\t\t\t\tok = notify.Wait(ctx)\n\t\t\t\tawake <- g\n\t\t\t}\n\t\t\tlocker.Unlock()\n\t\t}(i)\n\t}\n\tfor i := 0; i < n; i++ {\n\t\tfor j := 0; j < n; j++ {\n\t\t\t<-running // Will deadlock unless n are running.\n\t\t}\n\n\t\tselect {\n\t\tcase g := <-awake:\n\t\t\trequire.FailNow(t, \"goroutine should be asleep\", \"goroutine #%d not asleep\", g)\n\t\tdefault:\n\t\t}\n\n\t\tlocker.Lock()\n\t\tif i == n-1 {\n\t\t\tcancel()\n\t\t} else {\n\t\t\tnotify.Broadcast()\n\t\t}\n\t\tlocker.Unlock()\n\n\t\tseen := make([]bool, n)\n\t\tfor j := 0; j < n; j++ {\n\t\t\tg := <-awake\n\t\t\trequire.Falsef(t, seen[g], \"goroutine #%d woke up twice\", g)\n\t\t\tseen[g] = true\n\t\t}\n\t}\n\n\tselect {\n\tcase g := <-running:\n\t\trequire.FailNow(t, \"goroutine should not be running\", \"goroutine #%d still running\", g)\n\tdefault:\n\t}\n}\n"
  },
  {
    "path": "internal/queue/metrics.go",
    "content": "package queue\n\ntype MetricsTracer[T any] interface {\n\tItemQueued(name string, item T)\n\tItemPop(name string, item T)\n}\n\nvar _ MetricsTracer[any] = (*noopTracer[any])(nil)\n\ntype noopTracer[T any] struct{}\n\n// nolint:revive\nfunc (*noopTracer[T]) ItemQueued(name string, item T) {}\n\n// nolint:revive\nfunc (*noopTracer[T]) ItemPop(name string, item T) {}\n"
  },
  {
    "path": "internal/queue/priority.go",
    "content": "package queue\n\nimport (\n\t\"container/heap\"\n\t\"sync\"\n)\n\ntype ICounter interface {\n\tCounter() uint64\n}\n\n// A priorityMessageQueue implements heap.Interface and holds Items.\ntype PriorityQueue[T ICounter] struct {\n\tname    string\n\tmetrics MetricsTracer[T]\n\n\titems      []T\n\tmuMessages sync.RWMutex\n}\n\nfunc NewPriorityQueue[T ICounter](name string, tracer MetricsTracer[T]) *PriorityQueue[T] {\n\tqueue := &PriorityQueue[T]{\n\t\tname:    name,\n\t\tmetrics: tracer,\n\n\t\titems: []T{},\n\t}\n\n\theap.Init(queue)\n\treturn queue\n}\n\nfunc (pq *PriorityQueue[T]) Add(m T) {\n\tpq.muMessages.Lock()\n\theap.Push(pq, m)\n\tpq.metrics.ItemQueued(pq.name, m)\n\tpq.muMessages.Unlock()\n}\n\nfunc (pq *PriorityQueue[T]) NextAll(cb func(next T) error) error {\n\tpq.muMessages.Lock()\n\tdefer pq.muMessages.Unlock()\n\n\tfor len(pq.items) > 0 {\n\t\titem := heap.Pop(pq).(T)\n\t\tif err := cb(item); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (pq *PriorityQueue[T]) Next() (item T) {\n\tpq.muMessages.Lock()\n\tif len(pq.items) > 0 {\n\t\titem = heap.Pop(pq).(T)\n\t}\n\tpq.muMessages.Unlock()\n\treturn\n}\n\nfunc (pq *PriorityQueue[T]) Size() (l int) {\n\tpq.muMessages.RLock()\n\tl = pq.Len()\n\tpq.muMessages.RUnlock()\n\treturn\n}\n\nfunc (pq *PriorityQueue[T]) Len() (l int) {\n\tl = len(pq.items)\n\treturn\n}\n\nfunc (pq *PriorityQueue[T]) Less(i, j int) bool {\n\t// We want Pop to give us the lowest, not highest, priority so we use lower than here.\n\treturn pq.items[i].Counter() < pq.items[j].Counter()\n}\n\nfunc (pq *PriorityQueue[T]) Swap(i, j int) {\n\tpq.items[i], pq.items[j] = pq.items[j], pq.items[i]\n}\n\nfunc (pq *PriorityQueue[T]) Push(x any) {\n\tpq.items = append(pq.items, x.(T))\n\tpq.metrics.ItemQueued(pq.name, x.(T))\n}\n\nfunc (pq *PriorityQueue[T]) Pop() (item any) {\n\tvar null T\n\tif n := len(pq.items); n > 0 {\n\t\titem = pq.items[n-1]\n\t\tpq.metrics.ItemPop(pq.name, item.(T))\n\t\tpq.items, pq.items[n-1] = pq.items[:n-1], null\n\t}\n\treturn item\n}\n"
  },
  {
    "path": "internal/queue/simple.go",
    "content": "package queue\n\nimport (\n\t\"container/list\"\n\t\"context\"\n\t\"sync\"\n)\n\ntype SimpleQueue[T any] struct {\n\tname    string\n\tlist    *list.List\n\tmetrics MetricsTracer[T]\n\tsignal  chan struct{}\n\tmu      sync.Mutex\n}\n\nfunc NewSimpleQueue[T any](name string, tracer MetricsTracer[T]) *SimpleQueue[T] {\n\treturn &SimpleQueue[T]{\n\t\tname:    name,\n\t\tmetrics: tracer,\n\t\tlist:    list.New(),\n\t\tsignal:  make(chan struct{}),\n\t}\n}\n\n// Add pushes an item to the queue\nfunc (q *SimpleQueue[T]) Add(m T) {\n\tq.mu.Lock()\n\tdefer q.mu.Unlock()\n\n\t_ = q.list.PushBack(m)\n\tq.metrics.ItemQueued(q.name, m)\n\n\t// signal that we got a new item\n\tselect {\n\tcase q.signal <- struct{}{}:\n\tdefault:\n\t}\n}\n\n// Pop removes and returns the first item from the queue.\n// If the queue is empty, the second returned value will be false.\nfunc (q *SimpleQueue[T]) Pop() (m T, ok bool) {\n\tq.mu.Lock()\n\tdefer q.mu.Unlock()\n\n\tif q.list.Len() > 0 {\n\t\telement := q.list.Front()\n\t\tq.list.Remove(element)\n\t\tm = element.Value.(T)\n\t\tok = true\n\t}\n\n\treturn\n}\n\n// WaitForItem blocks until a new item is available or the context is canceled.\n// It returns the new item along with a boolean value indicating whether context has expired.\nfunc (q *SimpleQueue[T]) WaitForItem(ctx context.Context) (item T, ok bool) {\n\tq.mu.Lock()\n\tdefer q.mu.Unlock()\n\n\t// Keep attempting to retrieve a new item until the context is canceled\n\tfor ctx.Err() == nil {\n\t\tif q.list.Len() == 0 {\n\t\t\t// queue is empty, wait for either a signal of a new item or a\n\t\t\t// context cancellation\n\t\t\tq.mu.Unlock()\n\t\t\tselect {\n\t\t\tcase <-q.signal:\n\t\t\tcase <-ctx.Done():\n\t\t\t}\n\t\t\tq.mu.Lock()\n\n\t\t\tcontinue\n\t\t}\n\n\t\t// pop front item from the queue\n\t\telement := q.list.Front()\n\t\tq.list.Remove(element)\n\n\t\treturn element.Value.(T), true\n\t}\n\n\treturn\n}\n"
  },
  {
    "path": "internal/queue/simple_test.go",
    "content": "package queue\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\ntype testSimpleQueue = *SimpleQueue[int]\n\nfunc newTestSimpleQueue() testSimpleQueue {\n\treturn NewSimpleQueue[int](\"test\", &noopTracer[int]{})\n}\n\nfunc TestQueue(t *testing.T) {\n\tqueue := newTestSimpleQueue()\n\n\te, ok := queue.Pop()\n\trequire.Equal(t, 0, e)\n\trequire.False(t, ok)\n\n\tqueue.Add(1)\n\te, ok = queue.Pop()\n\trequire.Equal(t, 1, e)\n\trequire.True(t, ok)\n\n\te, ok = queue.Pop()\n\trequire.Equal(t, 0, e)\n\trequire.False(t, ok)\n}\n\nfunc TestSyncQueue(t *testing.T) {\n\tcases := []struct{ N int }{\n\t\t{1}, {10}, {100}, {1000}, {10000},\n\t}\n\n\tfor _, tc := range cases {\n\t\tname := fmt.Sprintf(\"%d_elements\", tc.N)\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tqueue := newTestSimpleQueue()\n\n\t\t\tfor i := 0; i < tc.N; i++ {\n\t\t\t\tqueue.Add(i + 1)\n\t\t\t}\n\n\t\t\tfor i := 0; i < tc.N; i++ {\n\t\t\t\te, ok := queue.Pop()\n\t\t\t\trequire.Equal(t, i+1, e)\n\t\t\t\trequire.True(t, ok)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestAsyncQueue(t *testing.T) {\n\tcases := []struct{ N int }{\n\t\t{1}, {10}, {100}, {1000}, {10000},\n\t}\n\n\tfor _, tc := range cases {\n\t\tname := fmt.Sprintf(\"%d_elements\", tc.N)\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tqueue := newTestSimpleQueue()\n\n\t\t\twg := sync.WaitGroup{}\n\n\t\t\twg.Add(tc.N)\n\t\t\telems := map[int]struct{}{}\n\t\t\tfor i := 0; i < tc.N; i++ {\n\t\t\t\telems[i+1] = struct{}{}\n\t\t\t\tgo func(i int) {\n\t\t\t\t\tqueue.Add(i + 1)\n\t\t\t\t\twg.Done()\n\t\t\t\t}(i)\n\t\t\t}\n\n\t\t\twg.Wait()\n\n\t\t\tfor i := 0; i < tc.N; i++ {\n\t\t\t\te, ok := queue.Pop()\n\t\t\t\trequire.True(t, ok)\n\n\t\t\t\t_, exist := elems[e]\n\t\t\t\trequire.True(t, exist)\n\t\t\t\tdelete(elems, e)\n\t\t\t}\n\n\t\t\trequire.Len(t, elems, 0)\n\t\t})\n\t}\n}\n\nfunc TestWaitnForItemQueue(t *testing.T) {\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\tcases := []struct{ N int }{\n\t\t{1}, {10}, {100}, {1000}, {10000},\n\t}\n\n\tfor _, tc := range cases {\n\t\tname := fmt.Sprintf(\"%d_elements\", tc.N)\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tctx, cancel := context.WithCancel(ctx)\n\t\t\tdefer cancel()\n\n\t\t\tqueue := newTestSimpleQueue()\n\n\t\t\tcc := make(chan int, tc.N)\n\t\t\tgo func() {\n\t\t\t\tdefer close(cc)\n\t\t\t\tfor {\n\t\t\t\t\te, ok := queue.WaitForItem(ctx)\n\t\t\t\t\tif !ok {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\tcc <- e\n\t\t\t\t}\n\t\t\t}()\n\n\t\t\tfor i := 0; i < tc.N; i++ {\n\t\t\t\tqueue.Add(i + 1)\n\t\t\t}\n\n\t\t\tfor i := 0; i < tc.N; i++ {\n\t\t\t\tselect {\n\t\t\t\tcase e := <-cc:\n\t\t\t\t\trequire.Equal(t, i+1, e)\n\t\t\t\tcase <-time.After(time.Second):\n\t\t\t\t\trequire.FailNow(t, \"timeout while waiting for event\")\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "internal/sysutil/sysutil.go",
    "content": "package sysutil\n\nimport (\n\t\"os\"\n\t\"runtime\"\n\t\"syscall\"\n\n\t\"go.uber.org/multierr\"\n\t\"moul.io/openfiles\"\n\n\t\"berty.tech/weshnet/v2/internal/bertyversion\"\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n\t\"berty.tech/weshnet/v2/pkg/username\"\n)\n\nfunc SystemInfoProcess() (*protocoltypes.SystemInfo_Process, error) {\n\tvar errs error\n\n\t// openfiles\n\tnofile, nofileErr := openfiles.Count()\n\terrs = multierr.Append(errs, nofileErr)\n\n\t// hostname\n\thn, err := os.Hostname()\n\terrs = multierr.Append(errs, err)\n\n\t// working dir\n\twd, err := syscall.Getwd()\n\terrs = multierr.Append(errs, err)\n\n\treply := protocoltypes.SystemInfo_Process{\n\t\tNofile:           nofile,\n\t\tTooManyOpenFiles: openfiles.IsTooManyError(nofileErr),\n\t\tNumCpu:           int64(runtime.NumCPU()),\n\t\tGoVersion:        runtime.Version(),\n\t\tHostName:         hn,\n\t\tNumGoroutine:     int64(runtime.NumGoroutine()),\n\t\tOperatingSystem:  runtime.GOOS,\n\t\tArch:             runtime.GOARCH,\n\t\tVersion:          bertyversion.Version,\n\t\tVcsRef:           bertyversion.VcsRef,\n\t\tPid:              int64(syscall.Getpid()),\n\t\tUid:              int64(syscall.Getuid()),\n\t\tPpid:             int64(syscall.Getppid()),\n\t\tWorkingDir:       wd,\n\t\tSystemUsername:   username.GetUsername(),\n\t}\n\n\t// see sysutil_<OS>.go files\n\terr = appendCustomSystemInfo(&reply)\n\terrs = multierr.Append(errs, err)\n\n\treturn &reply, errs\n}\n"
  },
  {
    "path": "internal/sysutil/sysutil_unix.go",
    "content": "//go:build linux || darwin\n\npackage sysutil\n\nimport (\n\t\"syscall\"\n\n\t\"go.uber.org/multierr\"\n\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n)\n\nfunc appendCustomSystemInfo(reply *protocoltypes.SystemInfo_Process) error {\n\tvar errs error\n\n\t// rlimit\n\trlimitNofile := syscall.Rlimit{}\n\terr := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rlimitNofile)\n\terrs = multierr.Append(errs, err)\n\treply.RlimitCur = rlimitNofile.Cur\n\treply.RlimitMax = rlimitNofile.Max\n\n\t// rusage\n\trusage := syscall.Rusage{}\n\terr = syscall.Getrusage(syscall.RUSAGE_SELF, &rusage)\n\terrs = multierr.Append(errs, err)\n\treply.UserCpuTimeMs = int64(rusage.Utime.Sec*1000) + int64(rusage.Utime.Usec/1000)   // nolint:unconvert // on some archs, those vars may be int32 instead of int64\n\treply.SystemCpuTimeMs = int64(rusage.Stime.Sec*1000) + int64(rusage.Stime.Usec/1000) // nolint:unconvert // on some archs, those vars may be int32 instead of int64\n\n\t// process priority\n\tprio, err := syscall.Getpriority(syscall.PRIO_PROCESS, 0)\n\terrs = multierr.Append(errs, err)\n\treply.Priority = int64(prio)\n\n\treturn errs\n}\n"
  },
  {
    "path": "internal/sysutil/sysutil_unsupported.go",
    "content": "//go:build !linux && !darwin\n\npackage sysutil\n\nimport \"berty.tech/weshnet/v2/pkg/protocoltypes\"\n\nfunc appendCustomSystemInfo(reply *protocoltypes.SystemInfo_Process) error {\n\treturn nil\n}\n"
  },
  {
    "path": "internal/tools/example_test.go",
    "content": "package tools_test\n"
  },
  {
    "path": "internal/tools/tools.go",
    "content": "//go:build tools\n\n// Package tools ensures that `go mod` detect some required dependencies.\n//\n// This package should not be imported directly.\npackage tools\n\nimport (\n\t// build tool\n\t_ \"github.com/buicongtan1997/protoc-gen-swagger-config\"\n\t// required by Makefile\n\t_ \"github.com/daixiang0/gci\"\n\t// required by protoc\n\t_ \"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway\"\n\t// required by protoc\n\t_ \"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger\"\n\t// required by Makefile\n\t_ \"github.com/mdomke/git-semver/v5\"\n\t// required by protoc\n\t_ \"github.com/pseudomuto/protoc-gen-doc/cmd/protoc-gen-doc\"\n\t// required by protoc\n\t_ \"github.com/srikrsna/protoc-gen-gotag\"\n\t// required by protoc\n\t_ \"github.com/srikrsna/protoc-gen-gotag/tagger\"\n\t// required by protoc\n\t_ \"golang.org/x/tools/cmd/goimports\"\n\t// required by protoc\n\t_ \"google.golang.org/grpc/cmd/protoc-gen-go-grpc\"\n\t// required by protoc\n\t_ \"google.golang.org/protobuf/cmd/protoc-gen-go\"\n\t// required by protoc\n\t_ \"google.golang.org/protobuf/proto\"\n\t// required by Makefile\n\t_ \"moul.io/testman\"\n\t// required by Makefile\n\t_ \"mvdan.cc/gofumpt\"\n)\n"
  },
  {
    "path": "internal/tools/tools_untool.go",
    "content": "//go:build !tools\n\n// Package tools ensures that `go mod` detect some required dependencies.\n//\n// This package should not be imported directly.\n//\n// This file is a noop to make `go list` happy.\npackage tools\n"
  },
  {
    "path": "message_marshaler.go",
    "content": "package weshnet\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"sync\"\n\n\t\"github.com/libp2p/go-libp2p/core/crypto\"\n\tpeer \"github.com/libp2p/go-libp2p/core/peer\"\n\t\"google.golang.org/protobuf/proto\"\n\n\t\"berty.tech/go-ipfs-log/enc\"\n\t\"berty.tech/go-ipfs-log/entry\"\n\t\"berty.tech/go-orbit-db/iface\"\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n\t\"berty.tech/weshnet/v2/pkg/rendezvous\"\n\t\"berty.tech/weshnet/v2/pkg/secretstore\"\n)\n\ntype PeerDeviceGroup struct {\n\tGroup    *protocoltypes.Group\n\tDevicePK crypto.PubKey\n}\n\ntype OrbitDBMessageMarshaler struct {\n\trp           *rendezvous.RotationInterval\n\tsharedKeys   map[string]enc.SharedKey\n\ttopicGroup   map[string]*protocoltypes.Group\n\tdeviceCaches map[peer.ID]*PeerDeviceGroup\n\tmuMarshall   sync.RWMutex\n\tselfid       peer.ID\n\tsecretStore  secretstore.SecretStore\n\n\t// in Replication Mode DeviceKey should not be sent\n\tuseReplicationMode bool\n}\n\nfunc NewOrbitDBMessageMarshaler(selfid peer.ID, secretStore secretstore.SecretStore, rp *rendezvous.RotationInterval, useReplicationMode bool) *OrbitDBMessageMarshaler {\n\treturn &OrbitDBMessageMarshaler{\n\t\tselfid:             selfid,\n\t\tsharedKeys:         make(map[string]enc.SharedKey),\n\t\tdeviceCaches:       make(map[peer.ID]*PeerDeviceGroup),\n\t\ttopicGroup:         make(map[string]*protocoltypes.Group),\n\t\trp:                 rp,\n\t\tsecretStore:        secretStore,\n\t\tuseReplicationMode: useReplicationMode,\n\t}\n}\n\nfunc (m *OrbitDBMessageMarshaler) RegisterSharedKeyForTopic(topic string, sk enc.SharedKey) {\n\tm.muMarshall.Lock()\n\tm.sharedKeys[topic] = sk\n\tm.muMarshall.Unlock()\n}\n\nfunc (m *OrbitDBMessageMarshaler) RegisterGroup(sid string, group *protocoltypes.Group) {\n\tm.muMarshall.Lock()\n\tm.topicGroup[sid] = group\n\tm.muMarshall.Unlock()\n}\n\nfunc (m *OrbitDBMessageMarshaler) GetDevicePKForPeerID(id peer.ID) (pdg *PeerDeviceGroup, ok bool) {\n\tm.muMarshall.RLock()\n\tpdg, ok = m.deviceCaches[id]\n\tm.muMarshall.RUnlock()\n\treturn\n}\n\nfunc (m *OrbitDBMessageMarshaler) getSharedKeyFor(topic string) (sk enc.SharedKey, ok bool) {\n\tsk, ok = m.sharedKeys[topic]\n\treturn\n}\n\nfunc (m *OrbitDBMessageMarshaler) Marshal(msg *iface.MessageExchangeHeads) ([]byte, error) {\n\ttopic := msg.Address\n\n\tm.muMarshall.RLock()\n\tdefer m.muMarshall.RUnlock()\n\n\t// marshall binary always return nil has error\n\tpid, _ := m.selfid.MarshalBinary()\n\n\tpoint, err := m.rp.PointForTopic(topic)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to get rendezvous for period: %w\", err)\n\t}\n\n\tgroup, ok := m.topicGroup[topic]\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"unknown group for topic: %s\", topic)\n\t}\n\n\tvar ownPK []byte\n\n\t// in replication mode, it doesn't make sense to send DevicePK\n\tif !m.useReplicationMode {\n\t\townDevice, err := m.secretStore.GetOwnMemberDeviceForGroup(group)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"unable to get own member device key for group: %w\", err)\n\t\t}\n\n\t\townPK, err = ownDevice.Device().Raw()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"unable to get raw pk for device: %w\", err)\n\t\t}\n\t}\n\n\t// @TODO(gfanton): use protobuf for this ?\n\theads, err := json.Marshal(msg.Heads)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to marshal heads: %w\", err)\n\t}\n\n\tbox := &protocoltypes.OrbitDBMessageHeads_Box{\n\t\tAddress:  msg.Address,\n\t\tHeads:    heads,\n\t\tDevicePk: ownPK,\n\t\tPeerId:   pid,\n\t}\n\n\tsealedBox, err := m.sealBox(msg.Address, box)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to seal box: %w\", err)\n\t}\n\n\tmsghead := protocoltypes.OrbitDBMessageHeads{\n\t\tRawRotation: point.RawRotationTopic(),\n\t\tSealedBox:   sealedBox,\n\t}\n\n\tpayload, err := proto.Marshal(&msghead)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to marshal payload: %w\", err)\n\t}\n\n\treturn payload, nil\n}\n\nfunc (m *OrbitDBMessageMarshaler) Unmarshal(payload []byte, msg *iface.MessageExchangeHeads) error {\n\tm.muMarshall.Lock()\n\tdefer m.muMarshall.Unlock()\n\n\tif msg == nil {\n\t\tmsg = &iface.MessageExchangeHeads{}\n\t}\n\n\tmsghead := protocoltypes.OrbitDBMessageHeads{}\n\tif err := proto.Unmarshal(payload, &msghead); err != nil {\n\t\treturn fmt.Errorf(\"unable to unmarshal payload `%x`: %w\", payload, err)\n\t}\n\n\trotation := msghead.GetRawRotation()\n\tpoint, err := m.rp.PointForRawRotation(rotation)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to get topic for rendezvous: %w\", err)\n\t}\n\n\tbox, err := m.openBox(point.Topic(), msghead.GetSealedBox())\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to open sealed box: %w\", err)\n\t}\n\n\tvar entries []*entry.Entry\n\tif err := json.Unmarshal(box.Heads, &entries); err != nil {\n\t\treturn fmt.Errorf(\"unable to unmarshal entries: %w\", err)\n\t}\n\n\tmsg.Address = box.Address\n\tmsg.Heads = entries\n\n\tif box.DevicePk == nil {\n\t\t// @NOTE(gfanton): this is probably a message from a replication server\n\t\t// which should not have a DevicePK\n\t\treturn nil\n\t}\n\n\tpid, err := peer.IDFromBytes(box.PeerId)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to parse peer id: %w\", err)\n\t}\n\n\t// store device into cache\n\tvar pdg PeerDeviceGroup\n\n\tpub, err := crypto.UnmarshalEd25519PublicKey(box.DevicePk)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to unmarshal remote device pk: %w\", err)\n\t}\n\n\tpdg.DevicePK = pub\n\tgroup, ok := m.topicGroup[msg.Address]\n\tif ok {\n\t\t// @FIXME(gfanton): do we need to raise an error here ?\n\t\tpdg.Group = group\n\t}\n\tm.deviceCaches[pid] = &pdg\n\n\treturn nil\n}\n\nfunc (m *OrbitDBMessageMarshaler) sealBox(topic string, box *protocoltypes.OrbitDBMessageHeads_Box) ([]byte, error) {\n\tsk, ok := m.getSharedKeyFor(topic)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"unable to get shared key for topic\")\n\t}\n\n\trawBox, err := proto.Marshal(box)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to marshal box %w\", err)\n\t}\n\n\tsealedBox, err := sk.Seal(rawBox)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to seal box: %w\", err)\n\t}\n\n\treturn sealedBox, nil\n}\n\nfunc (m *OrbitDBMessageMarshaler) openBox(topic string, payload []byte) (*protocoltypes.OrbitDBMessageHeads_Box, error) {\n\tsk, ok := m.getSharedKeyFor(topic)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"unable to get shared key for topic\")\n\t}\n\n\trawBox, err := sk.Open(payload)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to open sealed box: %w\", err)\n\t}\n\n\tbox := &protocoltypes.OrbitDBMessageHeads_Box{}\n\tif err := proto.Unmarshal(rawBox, box); err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to unmarshal box: %w\", err)\n\t}\n\n\treturn box, nil\n}\n"
  },
  {
    "path": "message_marshaler_test.go",
    "content": "package weshnet\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\tmocknet \"github.com/libp2p/go-libp2p/p2p/net/mock\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"berty.tech/go-ipfs-log/enc\"\n\t\"berty.tech/go-ipfs-log/entry\"\n\t\"berty.tech/go-orbit-db/iface\"\n\t\"berty.tech/weshnet/v2/pkg/rendezvous\"\n\t\"berty.tech/weshnet/v2/pkg/secretstore\"\n)\n\nvar (\n\ttestSeed1 = []byte(\"secretsecretsecretsecretsecretse\") // 32 bytes seed\n\ttestSeed2 = []byte(\"badbadbadbadbadbadbadbadbadbadba\") // 32 bytes seed\n)\n\nfunc TestRotationMessageMarshaler(t *testing.T) {\n\tkey, err := enc.NewSecretbox(testSeed1)\n\trequire.NoError(t, err)\n\n\tmsg := &iface.MessageExchangeHeads{\n\t\tAddress: \"address_1\",\n\t\tHeads:   []*entry.Entry{},\n\t}\n\n\tmn := mocknet.New()\n\tdefer mn.Close()\n\n\tp, err := mn.GenPeer()\n\trequire.NoError(t, err)\n\n\t// generate keystore\n\tacc1, err := secretstore.NewInMemSecretStore(nil)\n\trequire.NoError(t, err)\n\n\trp := rendezvous.NewStaticRotationInterval()\n\tm := NewOrbitDBMessageMarshaler(p.ID(), acc1, rp, false)\n\n\tg, _, err := NewGroupMultiMember()\n\trequire.NoError(t, err)\n\n\tm.RegisterGroup(msg.Address, g)\n\n\trp.RegisterRotation(time.Now(), msg.Address, testSeed1)\n\tm.RegisterSharedKeyForTopic(msg.Address, key)\n\n\t// marshal with register topic, should succeed\n\tpayload, err := m.Marshal(msg)\n\trequire.NoError(t, err)\n\trequire.NotNil(t, payload)\n\n\tret := iface.MessageExchangeHeads{}\n\terr = m.Unmarshal(payload, &ret)\n\trequire.NoError(t, err)\n\n\tassert.Equal(t, msg.Address, ret.Address)\n}\n\nfunc TestRotationMessageMarshalUnknownTopic(t *testing.T) {\n\tmn := mocknet.New()\n\tdefer mn.Close()\n\n\tmsg := &iface.MessageExchangeHeads{\n\t\tAddress: \"address_1\",\n\t\tHeads:   []*entry.Entry{},\n\t}\n\n\tp, err := mn.GenPeer()\n\trequire.NoError(t, err)\n\n\t// generate keystore\n\tacc, err := secretstore.NewInMemSecretStore(nil)\n\trequire.NoError(t, err)\n\n\trp := rendezvous.NewStaticRotationInterval()\n\tm := NewOrbitDBMessageMarshaler(p.ID(), acc, rp, false)\n\n\t// marshal without register topic, should fail\n\tpayload, err := m.Marshal(msg)\n\trequire.Error(t, err)\n\trequire.Nil(t, payload)\n}\n\nfunc TestRotationMessageUnmarshalUnknownTopic(t *testing.T) {\n\tmn := mocknet.New()\n\tdefer mn.Close()\n\n\tmsg := &iface.MessageExchangeHeads{\n\t\tAddress: \"address_1\",\n\t\tHeads:   []*entry.Entry{},\n\t}\n\tkey1, err := enc.NewSecretbox(testSeed1)\n\trequire.NoError(t, err)\n\n\tkey2, err := enc.NewSecretbox(testSeed2)\n\trequire.NoError(t, err)\n\n\tp1, err := mn.GenPeer()\n\trequire.NoError(t, err)\n\n\tp2, err := mn.GenPeer()\n\trequire.NoError(t, err)\n\n\t// generate keystore\n\tacc1, err := secretstore.NewInMemSecretStore(nil)\n\trequire.NoError(t, err)\n\n\tacc2, err := secretstore.NewInMemSecretStore(nil)\n\trequire.NoError(t, err)\n\n\tg1, _, err := NewGroupMultiMember()\n\trequire.NoError(t, err)\n\n\tg2, _, err := NewGroupMultiMember()\n\trequire.NoError(t, err)\n\n\trp1 := rendezvous.NewStaticRotationInterval()\n\trp1.RegisterRotation(time.Now(), msg.Address, testSeed1)\n\n\trp2 := rendezvous.NewStaticRotationInterval()\n\n\tm1 := NewOrbitDBMessageMarshaler(p1.ID(), acc1, rp1, false)\n\tm1.RegisterSharedKeyForTopic(msg.Address, key1)\n\n\tm1.RegisterGroup(msg.Address, g1)\n\n\tpayload, err := m1.Marshal(msg)\n\trequire.NoError(t, err)\n\n\tm2 := NewOrbitDBMessageMarshaler(p2.ID(), acc2, rp2, false)\n\tm2.RegisterSharedKeyForTopic(msg.Address, key2)\n\n\tm2.RegisterGroup(msg.Address, g2)\n\n\tvar ret iface.MessageExchangeHeads\n\n\t// marshal with wrong key should fail\n\terr = m2.Unmarshal(payload, &ret)\n\trequire.Error(t, err)\n\tassert.NotEqual(t, ret.Address, msg.Address)\n}\n\nfunc TestRotationMessageMarshalWrongKey(t *testing.T) {\n\tmn := mocknet.New()\n\tdefer mn.Close()\n\n\tmsg := &iface.MessageExchangeHeads{\n\t\tAddress: \"address_1\",\n\t\tHeads:   []*entry.Entry{},\n\t}\n\n\tkey1, err := enc.NewSecretbox(testSeed1)\n\trequire.NoError(t, err)\n\n\tkey2, err := enc.NewSecretbox(testSeed2)\n\trequire.NoError(t, err)\n\n\tp1, err := mn.GenPeer()\n\trequire.NoError(t, err)\n\n\tp2, err := mn.GenPeer()\n\trequire.NoError(t, err)\n\n\tg1, _, err := NewGroupMultiMember()\n\trequire.NoError(t, err)\n\n\tg2, _, err := NewGroupMultiMember()\n\trequire.NoError(t, err)\n\n\t// generate keystore\n\tacc1, err := secretstore.NewInMemSecretStore(nil)\n\trequire.NoError(t, err)\n\n\tacc2, err := secretstore.NewInMemSecretStore(nil)\n\trequire.NoError(t, err)\n\n\trp1 := rendezvous.NewStaticRotationInterval()\n\trp1.RegisterRotation(time.Now(), msg.Address, testSeed1)\n\trp2 := rendezvous.NewStaticRotationInterval()\n\trp2.RegisterRotation(time.Now(), msg.Address, testSeed2)\n\n\tm1 := NewOrbitDBMessageMarshaler(p1.ID(), acc1, rp1, false)\n\tm1.RegisterSharedKeyForTopic(msg.Address, key1)\n\tm1.RegisterGroup(msg.Address, g1)\n\n\tpayload, err := m1.Marshal(msg)\n\trequire.NoError(t, err)\n\n\tm2 := NewOrbitDBMessageMarshaler(p2.ID(), acc2, rp2, false)\n\tm2.RegisterSharedKeyForTopic(msg.Address, key2)\n\tm2.RegisterGroup(msg.Address, g2)\n\n\tvar ret iface.MessageExchangeHeads\n\n\t// marshal with wrong key should fail\n\terr = m2.Unmarshal(payload, &ret)\n\trequire.Error(t, err)\n\tassert.NotEqual(t, ret.Address, msg.Address)\n\n\t// marshal with good key should succeed\n\terr = m1.Unmarshal(payload, &ret)\n\trequire.NoError(t, err)\n\tassert.Equal(t, ret.Address, msg.Address)\n}\n"
  },
  {
    "path": "orbitdb.go",
    "content": "package weshnet\n\nimport (\n\t\"context\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/ipfs/go-cid\"\n\t\"github.com/ipfs/go-datastore\"\n\tds_sync \"github.com/ipfs/go-datastore/sync\"\n\tcoreiface \"github.com/ipfs/kubo/core/coreiface\"\n\t\"github.com/libp2p/go-libp2p/core/crypto\"\n\tpeer \"github.com/libp2p/go-libp2p/core/peer\"\n\t\"github.com/libp2p/go-libp2p/p2p/host/eventbus\"\n\t\"github.com/pkg/errors\"\n\t\"github.com/prometheus/client_golang/prometheus\"\n\t\"go.uber.org/zap\"\n\n\tipfslog \"berty.tech/go-ipfs-log\"\n\t\"berty.tech/go-ipfs-log/enc\"\n\t\"berty.tech/go-ipfs-log/entry\"\n\t\"berty.tech/go-ipfs-log/identityprovider\"\n\t\"berty.tech/go-ipfs-log/io\"\n\torbitdb \"berty.tech/go-orbit-db\"\n\t\"berty.tech/go-orbit-db/baseorbitdb\"\n\t\"berty.tech/go-orbit-db/iface\"\n\t\"berty.tech/go-orbit-db/pubsub/pubsubcoreapi\"\n\t\"berty.tech/go-orbit-db/stores\"\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n\t\"berty.tech/weshnet/v2/pkg/ipfsutil\"\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n\t\"berty.tech/weshnet/v2/pkg/rendezvous\"\n\t\"berty.tech/weshnet/v2/pkg/secretstore\"\n\t\"berty.tech/weshnet/v2/pkg/tyber\"\n)\n\ntype GroupOpenMode uint64\n\nconst (\n\tGroupOpenModeUndefined GroupOpenMode = iota\n\tGroupOpenModeReplicate\n\tGroupOpenModeWrite\n)\n\nvar _ = GroupOpenModeUndefined\n\ntype loggable interface {\n\tsetLogger(*zap.Logger)\n}\n\ntype NewOrbitDBOptions struct {\n\tbaseorbitdb.NewOrbitDBOptions\n\tDatastore          datastore.Batching\n\tSecretStore        secretstore.SecretStore\n\tRotationInterval   *rendezvous.RotationInterval\n\tPrometheusRegister prometheus.Registerer\n\n\tGroupMetadataStoreType string\n\tGroupMessageStoreType  string\n\tReplicationMode        bool\n}\n\nfunc (n *NewOrbitDBOptions) applyDefaults() {\n\tif n.Datastore == nil {\n\t\tn.Datastore = ds_sync.MutexWrap(datastore.NewMapDatastore())\n\t}\n\n\tif n.PrometheusRegister == nil {\n\t\tn.PrometheusRegister = prometheus.DefaultRegisterer\n\t}\n\n\tif n.Cache == nil {\n\t\tn.Cache = NewOrbitDatastoreCache(n.Datastore)\n\t}\n\n\tif n.Logger == nil {\n\t\tn.Logger = zap.NewNop()\n\t}\n\n\tif n.RotationInterval == nil {\n\t\tn.RotationInterval = rendezvous.NewStaticRotationInterval()\n\t}\n\n\tif n.Logger == nil {\n\t\tn.Logger = zap.NewNop()\n\t}\n\tn.Logger = n.Logger.Named(\"odb\")\n\n\tif n.GroupMetadataStoreType == \"\" {\n\t\tn.GroupMetadataStoreType = \"wesh_group_metadata\"\n\t}\n\n\tif n.GroupMessageStoreType == \"\" {\n\t\tn.GroupMessageStoreType = \"wesh_group_messages\"\n\t}\n}\n\ntype (\n\tGroupMap           = sync.Map\n\tGroupContextMap    = sync.Map\n\tGroupsSigPubKeyMap = sync.Map\n)\n\ntype WeshOrbitDB struct {\n\tbaseorbitdb.BaseOrbitDB\n\tkeyStore           *BertySignedKeyStore\n\tsecretStore        secretstore.SecretStore\n\tpubSub             iface.PubSubInterface\n\trotationInterval   *rendezvous.RotationInterval\n\tmessageMarshaler   *OrbitDBMessageMarshaler\n\treplicationMode    bool\n\tprometheusRegister prometheus.Registerer\n\n\tgroupMetadataStoreType string\n\tgroupMessageStoreType  string\n\n\tctx context.Context\n\t// FIXME(gfanton): use real map instead of sync.Map\n\tgroups          *GroupMap           // map[string]*protocoltypes.Group\n\tgroupContexts   *GroupContextMap    // map[string]*GroupContext\n\tgroupsSigPubKey *GroupsSigPubKeyMap // map[string]crypto.PubKey\n}\n\nfunc (s *WeshOrbitDB) registerGroupPrivateKey(g *protocoltypes.Group) error {\n\tgroupID := g.GroupIDAsString()\n\n\tgSigSK, err := g.GetSigningPrivKey()\n\tif err != nil {\n\t\treturn errcode.ErrCode_TODO.Wrap(err)\n\t}\n\n\tif err := s.SetGroupSigPubKey(groupID, gSigSK.GetPublic()); err != nil {\n\t\treturn errcode.ErrCode_TODO.Wrap(err)\n\t}\n\n\tif err := s.keyStore.SetKey(gSigSK); err != nil {\n\t\treturn errcode.ErrCode_TODO.Wrap(err)\n\t}\n\n\treturn nil\n}\n\nfunc (s *WeshOrbitDB) registerGroupSigningPubKey(g *protocoltypes.Group) error {\n\tgroupID := g.GroupIDAsString()\n\n\tvar gSigPK crypto.PubKey\n\n\tgSigSK, err := g.GetSigningPrivKey()\n\tif err == nil && gSigSK != nil {\n\t\tgSigPK = gSigSK.GetPublic()\n\t} else {\n\t\tgSigPK, err = g.GetSigningPubKey()\n\t\tif err != nil {\n\t\t\treturn errcode.ErrCode_TODO.Wrap(err)\n\t\t}\n\t}\n\n\tif err := s.SetGroupSigPubKey(groupID, gSigPK); err != nil {\n\t\treturn errcode.ErrCode_TODO.Wrap(err)\n\t}\n\n\treturn nil\n}\n\nfunc NewWeshOrbitDB(ctx context.Context, ipfs coreiface.CoreAPI, options *NewOrbitDBOptions) (*WeshOrbitDB, error) {\n\tvar err error\n\n\tif options == nil {\n\t\toptions = &NewOrbitDBOptions{}\n\t}\n\n\toptions.applyDefaults()\n\n\tks := &BertySignedKeyStore{}\n\toptions.Keystore = ks\n\toptions.Identity = &identityprovider.Identity{}\n\n\tself, err := ipfs.Key().Self(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif options.PubSub == nil {\n\t\toptions.PubSub = pubsubcoreapi.NewPubSub(ipfs, self.ID(), time.Second, options.Logger, options.Tracer)\n\t}\n\n\tmm := NewOrbitDBMessageMarshaler(self.ID(), options.SecretStore, options.RotationInterval, options.ReplicationMode)\n\toptions.MessageMarshaler = mm\n\n\torbitDB, err := baseorbitdb.NewOrbitDB(ctx, ipfs, &options.NewOrbitDBOptions)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_TODO.Wrap(err)\n\t}\n\n\tbertyDB := &WeshOrbitDB{\n\t\tctx:                    ctx,\n\t\tmessageMarshaler:       mm,\n\t\tBaseOrbitDB:            orbitDB,\n\t\tkeyStore:               ks,\n\t\tsecretStore:            options.SecretStore,\n\t\trotationInterval:       options.RotationInterval,\n\t\tpubSub:                 options.PubSub,\n\t\tgroups:                 &GroupMap{},\n\t\tgroupContexts:          &GroupContextMap{},    // map[string]*GroupContext\n\t\tgroupsSigPubKey:        &GroupsSigPubKeyMap{}, // map[string]crypto.PubKey\n\t\tgroupMetadataStoreType: options.GroupMetadataStoreType,\n\t\tgroupMessageStoreType:  options.GroupMessageStoreType,\n\t\treplicationMode:        options.ReplicationMode,\n\t\tprometheusRegister:     options.PrometheusRegister,\n\t}\n\n\tif err := bertyDB.RegisterAccessControllerType(NewSimpleAccessController); err != nil {\n\t\treturn nil, errcode.ErrCode_TODO.Wrap(err)\n\t}\n\tbertyDB.RegisterStoreType(bertyDB.groupMetadataStoreType, constructorFactoryGroupMetadata(bertyDB, options.Logger))\n\tbertyDB.RegisterStoreType(bertyDB.groupMessageStoreType, constructorFactoryGroupMessage(bertyDB, options.Logger))\n\n\treturn bertyDB, nil\n}\n\nfunc (s *WeshOrbitDB) openAccountGroup(ctx context.Context, options *orbitdb.CreateDBOptions, ipfsCoreAPI ipfsutil.ExtendedCoreAPI) (*GroupContext, error) {\n\tl := s.Logger()\n\n\tif options == nil {\n\t\toptions = &orbitdb.CreateDBOptions{}\n\t}\n\n\tif options.EventBus == nil {\n\t\toptions.EventBus = s.EventBus()\n\t}\n\n\tgroup, _, err := s.secretStore.GetGroupForAccount()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrOrbitDBOpen.Wrap(err)\n\t}\n\n\tl.Debug(\"Got account group\", tyber.FormatStepLogFields(ctx, []tyber.Detail{{Name: \"Group\", Description: group.String()}})...)\n\n\tgc, err := s.OpenGroup(ctx, group, options)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrGroupOpen.Wrap(err)\n\t}\n\tl.Debug(\"Opened account group\", tyber.FormatStepLogFields(ctx, []tyber.Detail{})...)\n\n\tgc.TagGroupContextPeers(ipfsCoreAPI, 84)\n\n\tif err := gc.ActivateGroupContext(nil); err != nil {\n\t\treturn nil, errcode.ErrCode_TODO.Wrap(err)\n\t}\n\tl.Debug(\"TagGroupContextPeers done\", tyber.FormatStepLogFields(ctx, []tyber.Detail{})...)\n\treturn gc, nil\n}\n\nfunc (s *WeshOrbitDB) setHeadsForGroup(ctx context.Context, g *protocoltypes.Group, metaHeads, messageHeads []cid.Cid) error {\n\tgroupID := g.GroupIDAsString()\n\n\tvar (\n\t\terr                    error\n\t\tmetaImpl, messagesImpl orbitdb.Store\n\t)\n\n\texistingGC, err := s.getGroupContext(groupID)\n\tif err != nil && !errcode.Is(err, errcode.ErrCode_ErrMissingMapKey) {\n\t\treturn errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\tif err == nil {\n\t\tmetaImpl = existingGC.metadataStore\n\t\tmessagesImpl = existingGC.messageStore\n\t}\n\tif metaImpl == nil || messagesImpl == nil {\n\t\ts.groups.Store(groupID, g)\n\n\t\tif err := s.registerGroupSigningPubKey(g); err != nil {\n\t\t\treturn errcode.ErrCode_ErrInternal.Wrap(err)\n\t\t}\n\n\t\ts.Logger().Debug(\"OpenGroup\", zap.Any(\"public key\", g.PublicKey), zap.Any(\"secret\", g.Secret), zap.Stringer(\"type\", g.GroupType))\n\n\t\tif metaImpl == nil {\n\t\t\tmetaImpl, err = s.storeForGroup(ctx, s, g, nil, s.groupMetadataStoreType, GroupOpenModeReplicate)\n\t\t\tif err != nil {\n\t\t\t\treturn errcode.ErrCode_ErrOrbitDBOpen.Wrap(err)\n\t\t\t}\n\n\t\t\tdefer func() { _ = metaImpl.Close() }()\n\t\t}\n\n\t\tif messagesImpl == nil {\n\t\t\tmessagesImpl, err = s.storeForGroup(ctx, s, g, nil, s.groupMessageStoreType, GroupOpenModeReplicate)\n\t\t\tif err != nil {\n\t\t\t\treturn errcode.ErrCode_ErrOrbitDBOpen.Wrap(err)\n\t\t\t}\n\n\t\t\tdefer func() { _ = messagesImpl.Close() }()\n\t\t}\n\t}\n\n\tif messagesImpl == nil {\n\t\treturn errcode.ErrCode_ErrInternal.Wrap(fmt.Errorf(\"message store is nil\"))\n\t}\n\n\tif metaImpl == nil {\n\t\treturn errcode.ErrCode_ErrInternal.Wrap(fmt.Errorf(\"metadata store is nil\"))\n\t}\n\n\tvar wg sync.WaitGroup\n\n\t// load and wait heads for metadata and message stores\n\twg.Add(2)\n\n\tgo func() {\n\t\t// load meta heads\n\t\tif err := s.loadHeads(ctx, metaImpl, metaHeads); err != nil {\n\t\t\ts.Logger().Error(\"unable to load metadata heads\", zap.Error(err))\n\t\t}\n\n\t\twg.Done()\n\t}()\n\n\tgo func() {\n\t\t// load message heads\n\t\tif err := s.loadHeads(ctx, messagesImpl, messageHeads); err != nil {\n\t\t\ts.Logger().Error(\"unable to load message heads\", zap.Error(err))\n\t\t}\n\n\t\twg.Done()\n\t}()\n\n\twg.Wait()\n\n\treturn nil\n}\n\nfunc (s *WeshOrbitDB) loadHeads(ctx context.Context, store iface.Store, heads []cid.Cid) (err error) {\n\tsub, err := store.EventBus().Subscribe(new(stores.EventReplicated),\n\t\teventbus.Name(\"weshnet/load-heads\"))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to subscribe to EventReplicated\")\n\t}\n\tdefer sub.Close()\n\n\t// check and generate missing entries if needed\n\theadsEntries := make([]ipfslog.Entry, len(heads))\n\tfor i, h := range heads {\n\t\tif _, ok := store.OpLog().Get(h); !ok {\n\t\t\theadsEntries[i] = &entry.Entry{Hash: h}\n\t\t}\n\t}\n\n\tif len(headsEntries) == 0 {\n\t\treturn nil\n\t}\n\n\tstore.Replicator().Load(ctx, headsEntries)\n\n\tfor found := 0; found < len(heads); {\n\t\t// wait for load to finish\n\t\tselect {\n\t\tcase e := <-sub.Out():\n\t\t\tevt := e.(stores.EventReplicated)\n\n\t\t\t// iterate over entries from replicated event to search for our heads\n\t\t\tfor _, headEntry := range headsEntries {\n\t\t\t\tfor _, evtEntry := range evt.Entries {\n\t\t\t\t\tif evtEntry.Equals(headEntry) {\n\t\t\t\t\t\tfound++\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\tcase <-s.ctx.Done():\n\t\t\treturn s.ctx.Err()\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (s *WeshOrbitDB) OpenGroup(ctx context.Context, g *protocoltypes.Group, options *orbitdb.CreateDBOptions) (*GroupContext, error) {\n\tif s.secretStore == nil {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"db open in naive mode\"))\n\t}\n\n\tgroupID := g.GroupIDAsString()\n\n\texistingGC, err := s.getGroupContext(groupID)\n\tif err != nil && !errcode.Is(err, errcode.ErrCode_ErrMissingMapKey) {\n\t\treturn nil, errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\tif err == nil {\n\t\treturn existingGC, nil\n\t}\n\n\ts.groups.Store(groupID, g)\n\n\tif err := s.registerGroupPrivateKey(g); err != nil {\n\t\treturn nil, err\n\t}\n\n\ts.Logger().Debug(\"OpenGroup\", tyber.FormatStepLogFields(s.ctx, tyber.ZapFieldsToDetails(zap.Any(\"public key\", g.PublicKey), zap.Any(\"secret\", g.Secret), zap.Stringer(\"type\", g.GroupType)))...)\n\n\tmemberDevice, err := s.secretStore.GetOwnMemberDeviceForGroup(g)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err)\n\t}\n\n\tmpkb, err := crypto.MarshalPublicKey(memberDevice.Member())\n\tif err != nil {\n\t\tmpkb = []byte{}\n\t}\n\ts.Logger().Debug(\"Got member device\", tyber.FormatStepLogFields(s.ctx, []tyber.Detail{{Name: \"DevicePublicKey\", Description: base64.RawURLEncoding.EncodeToString(mpkb)}})...)\n\n\t// Force secret generation if missing\n\tif _, err := s.secretStore.GetShareableChainKey(s.ctx, g, memberDevice.Member()); err != nil {\n\t\treturn nil, errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err)\n\t}\n\n\ts.Logger().Debug(\"Got device chain key\", tyber.FormatStepLogFields(s.ctx, []tyber.Detail{})...)\n\n\tmetaImpl, err := s.groupMetadataStore(ctx, g, options)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrOrbitDBOpen.Wrap(err)\n\t}\n\ts.messageMarshaler.RegisterGroup(metaImpl.Address().String(), g)\n\n\ts.Logger().Debug(\"Got metadata store\", tyber.FormatStepLogFields(s.ctx, []tyber.Detail{})...)\n\n\t// force to unshare the same EventBus between groupMetadataStore and groupMessageStore\n\t// to avoid having a bunch of events which are not for the correct group\n\tif options != nil && options.EventBus != nil {\n\t\toptions.EventBus = eventbus.NewBus(\n\t\t\teventbus.WithMetricsTracer(eventbus.NewMetricsTracer(eventbus.WithRegisterer(s.prometheusRegister))))\n\t}\n\n\tmessagesImpl, err := s.groupMessageStore(ctx, g, options)\n\tif err != nil {\n\t\tmetaImpl.Close()\n\t\treturn nil, errcode.ErrCode_ErrOrbitDBOpen.Wrap(err)\n\t}\n\ts.messageMarshaler.RegisterGroup(messagesImpl.Address().String(), g)\n\n\ts.Logger().Debug(\"Got message store\", tyber.FormatStepLogFields(s.ctx, []tyber.Detail{})...)\n\n\tgc := NewContextGroup(g, metaImpl, messagesImpl, s.secretStore, memberDevice, s.Logger())\n\n\ts.Logger().Debug(\"Created group context\", tyber.FormatStepLogFields(s.ctx, []tyber.Detail{})...)\n\n\ts.groupContexts.Store(groupID, gc)\n\n\ts.Logger().Debug(\"Stored group context\", tyber.FormatStepLogFields(s.ctx, []tyber.Detail{})...)\n\n\treturn gc, nil\n}\n\nfunc (s *WeshOrbitDB) OpenGroupReplication(ctx context.Context, g *protocoltypes.Group, options *orbitdb.CreateDBOptions) (iface.Store, iface.Store, error) {\n\tif g == nil || len(g.PublicKey) == 0 {\n\t\treturn nil, nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"missing group or group pubkey\"))\n\t}\n\n\tgroupID := g.GroupIDAsString()\n\n\tgc, err := s.getGroupContext(groupID)\n\tif err != nil && !errcode.Is(err, errcode.ErrCode_ErrMissingMapKey) {\n\t\treturn nil, nil, errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\tif err == nil {\n\t\treturn gc.metadataStore, gc.messageStore, nil\n\t}\n\n\ts.groups.Store(groupID, g)\n\n\tif err := s.registerGroupSigningPubKey(g); err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tmetadataStore, err := s.storeForGroup(ctx, s, g, options, s.groupMetadataStoreType, GroupOpenModeReplicate)\n\tif err != nil {\n\t\t_ = metadataStore.Close()\n\t\treturn nil, nil, errors.Wrap(err, \"unable to open database\")\n\t}\n\n\tmessageStore, err := s.storeForGroup(ctx, s, g, options, s.groupMessageStoreType, GroupOpenModeReplicate)\n\tif err != nil {\n\t\treturn nil, nil, errors.Wrap(err, \"unable to open database\")\n\t}\n\n\treturn metadataStore, messageStore, nil\n}\n\nfunc (s *WeshOrbitDB) getGroupContext(id string) (*GroupContext, error) {\n\tg, ok := s.groupContexts.Load(id)\n\tif !ok {\n\t\treturn nil, errcode.ErrCode_ErrMissingMapKey\n\t}\n\n\tgc, ok := g.(*GroupContext)\n\tif !ok {\n\t\ts.groupContexts.Delete(id)\n\t\treturn nil, errors.New(\"cannot cast object to GroupContext\")\n\t}\n\n\tif gc.IsClosed() {\n\t\ts.groupContexts.Delete(id)\n\t\treturn nil, errcode.ErrCode_ErrMissingMapKey\n\t}\n\n\treturn g.(*GroupContext), nil\n}\n\n// SetGroupSigPubKey registers a new group signature pubkey, mainly used to\n// replicate a store data without needing to access to its content\nfunc (s *WeshOrbitDB) SetGroupSigPubKey(groupID string, pubKey crypto.PubKey) error {\n\tif pubKey == nil {\n\t\treturn errcode.ErrCode_ErrInvalidInput\n\t}\n\n\ts.groupsSigPubKey.Store(groupID, pubKey)\n\n\treturn nil\n}\n\nfunc (s *WeshOrbitDB) storeForGroup(ctx context.Context, o iface.BaseOrbitDB, g *protocoltypes.Group, options *orbitdb.CreateDBOptions, storeType string, groupOpenMode GroupOpenMode) (iface.Store, error) {\n\tl := s.Logger()\n\n\tif options == nil {\n\t\toptions = &orbitdb.CreateDBOptions{}\n\t}\n\n\t// setup eventbus metrics\n\tif options.EventBus == nil {\n\t\toptions.EventBus = eventbus.NewBus(eventbus.WithMetricsTracer(eventbus.NewMetricsTracer(eventbus.WithRegisterer(s.prometheusRegister))))\n\t}\n\n\toptions, err := DefaultOrbitDBOptions(g, options, s.keyStore, storeType, groupOpenMode)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tl.Debug(\"Opening store\", tyber.FormatStepLogFields(ctx, []tyber.Detail{{Name: \"Group\", Description: g.String()}, {Name: \"Options\", Description: fmt.Sprint(options)}}, tyber.Status(tyber.Running))...)\n\n\toptions.StoreType = &storeType\n\tname := fmt.Sprintf(\"%s_%s\", g.GroupIDAsString(), storeType)\n\n\taddr, err := o.DetermineAddress(ctx, name, storeType, &orbitdb.DetermineAddressOptions{AccessController: options.AccessController})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\ts.messageMarshaler.RegisterGroup(addr.String(), g)\n\n\tlinkKey, err := g.GetLinkKeyArray()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif key := linkKey[:]; len(key) > 0 {\n\t\tsk, err := enc.NewSecretbox(key)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tcborIO := io.CBOR()\n\t\tcborIO.ApplyOptions(&io.CBOROptions{LinkKey: sk})\n\t\toptions.IO = cborIO\n\n\t\tl.Debug(\"opening store: register rotation\", zap.String(\"topic\", addr.String()))\n\n\t\ts.messageMarshaler.RegisterSharedKeyForTopic(addr.String(), sk)\n\t\ts.rotationInterval.RegisterRotation(time.Now(), addr.String(), key)\n\t}\n\n\tstore, err := o.Open(ctx, name, options)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrOrbitDBOpen.Wrap(err)\n\t}\n\n\tl.Debug(\"Loading store\", tyber.FormatStepLogFields(ctx, []tyber.Detail{{Name: \"Group\", Description: g.String()}, {Name: \"StoreType\", Description: store.Type()}, {Name: \"Store\", Description: store.Address().String()}}, tyber.Status(tyber.Running))...)\n\n\t_ = store.Load(ctx, -1)\n\n\tl.Debug(\"Loaded store\", tyber.FormatStepLogFields(ctx, []tyber.Detail{{Name: \"Group\", Description: g.String()}})...)\n\n\treturn store, nil\n}\n\nfunc (s *WeshOrbitDB) groupMetadataStore(ctx context.Context, g *protocoltypes.Group, options *orbitdb.CreateDBOptions) (*MetadataStore, error) {\n\tif options == nil {\n\t\toptions = &orbitdb.CreateDBOptions{}\n\t}\n\n\tl := s.Logger().Named(\"metadataStore\")\n\toptions.Logger = l\n\n\tl.Debug(\"Opening group metadata store\", tyber.FormatStepLogFields(ctx, []tyber.Detail{{Name: \"Group\", Description: g.String()}, {Name: \"Options\", Description: fmt.Sprint(options)}}, tyber.Status(tyber.Running))...)\n\n\tstore, err := s.storeForGroup(ctx, s, g, options, s.groupMetadataStoreType, GroupOpenModeWrite)\n\tif err != nil {\n\t\treturn nil, tyber.LogFatalError(ctx, l, \"Failed to get group store\", errors.Wrap(err, \"unable to open database\"))\n\t}\n\n\tl.Debug(\"Got group store\", tyber.FormatStepLogFields(ctx, []tyber.Detail{{Name: \"DBName\", Description: store.DBName()}})...)\n\n\tsStore, ok := store.(*MetadataStore)\n\tif !ok {\n\t\treturn nil, tyber.LogFatalError(ctx, l, \"Failed to cast group store\", errors.New(\"unable to cast store to metadata store\"))\n\t}\n\n\tl.Debug(\"Opened group metadata store\", tyber.FormatStepLogFields(ctx, []tyber.Detail{{Name: \"Group\", Description: g.String()}})...)\n\n\treturn sStore, nil\n}\n\nfunc (s *WeshOrbitDB) groupMessageStore(ctx context.Context, g *protocoltypes.Group, options *orbitdb.CreateDBOptions) (*MessageStore, error) {\n\tif options == nil {\n\t\toptions = &orbitdb.CreateDBOptions{}\n\t}\n\n\tl := s.Logger().Named(\"messageStore\")\n\toptions.Logger = l\n\n\tl.Debug(\"Opening group message store\", tyber.FormatStepLogFields(ctx, []tyber.Detail{{Name: \"Group\", Description: g.String()}, {Name: \"Options\", Description: fmt.Sprint(options)}}, tyber.Status(tyber.Running))...)\n\n\tstore, err := s.storeForGroup(ctx, s, g, options, s.groupMessageStoreType, GroupOpenModeWrite)\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"unable to open database\")\n\t}\n\n\tmStore, ok := store.(*MessageStore)\n\tif !ok {\n\t\treturn nil, errors.New(\"unable to cast store to message store\")\n\t}\n\n\treturn mStore, nil\n}\n\nfunc (s *WeshOrbitDB) getGroupFromOptions(options *iface.NewStoreOptions) (*protocoltypes.Group, error) {\n\tgroupIDs, err := options.AccessController.GetAuthorizedByRole(identityGroupIDKey)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_TODO.Wrap(err)\n\t}\n\n\tif len(groupIDs) != 1 {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput\n\t}\n\n\tg, ok := s.groups.Load(groupIDs[0])\n\tif !ok {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput\n\t}\n\n\ttyped, ok := g.(*protocoltypes.Group)\n\tif !ok {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput\n\t}\n\n\treturn typed, nil\n}\n\nfunc (s *WeshOrbitDB) IsGroupLoaded(groupID string) bool {\n\tgc, ok := s.groups.Load(groupID)\n\n\treturn ok && gc != nil\n}\n\nfunc (s *WeshOrbitDB) GetDevicePKForPeerID(id peer.ID) (pdg *PeerDeviceGroup, ok bool) {\n\treturn s.messageMarshaler.GetDevicePKForPeerID(id)\n}\n"
  },
  {
    "path": "orbitdb_datastore_cache.go",
    "content": "package weshnet\n\nimport (\n\t\"context\"\n\n\tdatastore \"github.com/ipfs/go-datastore\"\n\t\"github.com/ipfs/go-datastore/query\"\n\n\t\"berty.tech/go-orbit-db/address\"\n\t\"berty.tech/go-orbit-db/cache\"\n\t\"berty.tech/weshnet/v2/internal/datastoreutil\"\n)\n\ntype datastoreCache struct {\n\tds datastore.Batching\n}\n\n//nolint:revive\nfunc (d *datastoreCache) Load(directory string, dbAddress address.Address) (datastore.Datastore, error) {\n\treturn datastoreutil.NewNamespacedDatastore(d.ds, datastore.NewKey(dbAddress.String())), nil\n}\n\nfunc (d *datastoreCache) Close() error {\n\treturn nil\n}\n\n//nolint:revive\nfunc (d *datastoreCache) Destroy(directory string, dbAddress address.Address) error {\n\tkeys, err := datastoreutil.NewNamespacedDatastore(d.ds, datastore.NewKey(dbAddress.String())).Query(context.TODO(), query.Query{KeysOnly: true})\n\tif err != nil {\n\t\treturn nil\n\t}\n\n\tfor {\n\t\tval, hasValue := keys.NextSync()\n\t\tif !hasValue {\n\t\t\treturn nil\n\t\t}\n\n\t\tif err := d.ds.Delete(context.TODO(), datastore.NewKey(val.Key)); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n}\n\nfunc NewOrbitDatastoreCache(ds datastore.Batching) cache.Interface {\n\treturn &datastoreCache{\n\t\tds: datastoreutil.NewNamespacedDatastore(ds, datastore.NewKey(NamespaceOrbitDBDatastore)),\n\t}\n}\n\nvar _ cache.Interface = (*datastoreCache)(nil)\n"
  },
  {
    "path": "orbitdb_many_adds_berty_test.go",
    "content": "package weshnet\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"path\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\tsync_ds \"github.com/ipfs/go-datastore/sync\"\n\t\"github.com/juju/fslock\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/zap\"\n\n\t\"berty.tech/go-orbit-db/iface\"\n\t\"berty.tech/weshnet/v2/pkg/ipfsutil\"\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n\t\"berty.tech/weshnet/v2/pkg/secretstore\"\n\t\"berty.tech/weshnet/v2/pkg/testutil\"\n)\n\nfunc testAddBerty(ctx context.Context, t *testing.T, node ipfsutil.CoreAPIMock, g *protocoltypes.Group, pathBase string, storageKey []byte, storageSalt []byte, amountToAdd, amountCurrentlyPresent int) {\n\tt.Helper()\n\ttestutil.FilterSpeed(t, testutil.Fast)\n\tt.Logf(\"TestAddBerty: amountToAdd: %d, amountCurrentlyPresent: %d\\n\", amountToAdd, amountCurrentlyPresent)\n\n\tapi := node.API()\n\tctx, cancel := context.WithCancel(ctx)\n\tdefer cancel()\n\n\tlock := fslock.New(path.Join(pathBase, \"lock\"))\n\terr := lock.TryLock()\n\trequire.NoError(t, err)\n\n\tdefer lock.Unlock()\n\n\tbaseDS, err := GetRootDatastoreForPath(pathBase, storageKey, storageSalt, zap.NewNop())\n\trequire.NoError(t, err)\n\n\tbaseDS = sync_ds.MutexWrap(baseDS)\n\n\tdefer testutil.Close(t, baseDS)\n\n\tsecretStore, err := secretstore.NewSecretStore(baseDS, nil)\n\trequire.NoError(t, err)\n\tdefer secretStore.Close()\n\n\todb, err := NewWeshOrbitDB(ctx, api, &NewOrbitDBOptions{\n\t\tDatastore:   baseDS,\n\t\tSecretStore: secretStore,\n\t})\n\trequire.NoError(t, err)\n\n\tdefer testutil.Close(t, odb)\n\n\treplicate := false\n\tgc, err := odb.OpenGroup(ctx, g, &iface.CreateDBOptions{\n\t\tReplicate: &replicate,\n\t})\n\trequire.NoError(t, err)\n\tdefer gc.Close()\n\n\tdefer testutil.Close(t, gc)\n\n\twg := sync.WaitGroup{}\n\twg.Add(amountToAdd * 2)\n\n\tamountCurrentlyFound := 0\n\n\tmessages, err := gc.MessageStore().ListEvents(ctx, nil, nil, false)\n\trequire.NoError(t, err)\n\n\tfor range messages {\n\t\tamountCurrentlyFound++\n\t}\n\n\tsub, err := gc.MessageStore().EventBus().Subscribe(new(*protocoltypes.GroupMessageEvent))\n\trequire.NoError(t, err)\n\tdefer sub.Close()\n\n\t// Watch for incoming new messages\n\tgo func() {\n\t\tfor range sub.Out() {\n\t\t\twg.Done()\n\t\t}\n\t}()\n\n\t_, err = gc.MetadataStore().AddDeviceToGroup(ctx)\n\trequire.NoError(t, err)\n\n\tfor i := 0; i < amountToAdd; i++ {\n\t\t_, err := gc.MessageStore().AddMessage(ctx, []byte(fmt.Sprintf(\"%d\", i)))\n\t\trequire.NoError(t, err)\n\t\twg.Done()\n\t}\n\n\tdone := make(chan struct{})\n\tgo func() {\n\t\twg.Wait()\n\t\tclose(done)\n\t}()\n\n\tselect {\n\tcase <-done:\n\tcase <-time.After(30 * time.Second):\n\t}\n\n\trequire.Equal(t, amountCurrentlyPresent, amountCurrentlyFound)\n}\n\nfunc TestAddBerty(t *testing.T) {\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\tapi := ipfsutil.TestingCoreAPI(ctx, t)\n\n\tpathBase, err := os.MkdirTemp(\"\", \"manyaddstest\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tdefer os.RemoveAll(pathBase)\n\n\tg, _, err := NewGroupMultiMember()\n\trequire.NoError(t, err)\n\n\tstorageKey := []byte(\"42424242424242424242424242424242\")\n\tstorageSalt := []byte(\"2121212121212121\")\n\n\ttestAddBerty(ctx, t, api, g, pathBase, storageKey, storageSalt, 20, 0)\n\ttestAddBerty(ctx, t, api, g, pathBase, storageKey, storageSalt, 0, 20)\n\ttestAddBerty(ctx, t, api, g, pathBase, storageKey, storageSalt, 20, 20)\n\ttestAddBerty(ctx, t, api, g, pathBase, storageKey, storageSalt, 0, 40)\n\n\t// FIXME: use github.com/stretchr/testify/suite\n}\n"
  },
  {
    "path": "orbitdb_many_adds_test.go",
    "content": "package weshnet\n\nimport (\n\t\"context\"\n\tcrand \"crypto/rand\"\n\t\"fmt\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/libp2p/go-libp2p/core/crypto\"\n\n\torbitdb \"berty.tech/go-orbit-db\"\n\t\"berty.tech/weshnet/v2/pkg/cryptoutil\"\n\t\"berty.tech/weshnet/v2/pkg/ipfsutil\"\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n)\n\nfunc TestAdd(t *testing.T) {\n\tamount := 20 // speeding up tests, 2000 takes ~25 seconds\n\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\tipfs := ipfsutil.TestingCoreAPI(ctx, t)\n\n\tdir := \"./orbitdb/benchmarks\"\n\tdefer os.RemoveAll(dir)\n\n\torbit, err := orbitdb.NewOrbitDB(ctx, ipfs.API(), &orbitdb.NewOrbitDBOptions{Directory: &dir})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer orbit.Close()\n\n\tif err := orbit.RegisterAccessControllerType(NewSimpleAccessController); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tsigk, _, err := crypto.GenerateEd25519Key(crand.Reader)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tks := &BertySignedKeyStore{}\n\terr = ks.SetKey(sigk)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tsigkB, err := cryptoutil.SeedFromEd25519PrivateKey(sigk)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tpubkB, err := sigk.GetPublic().Raw()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tg := &protocoltypes.Group{PublicKey: pubkB, Secret: sigkB}\n\treplicate := false\n\topts, err := DefaultOrbitDBOptions(g, &orbitdb.CreateDBOptions{Replicate: &replicate}, ks, \"log\", GroupOpenModeWrite)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdb, err := orbit.Log(ctx, \"DemoLog\", opts)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tdefer db.Drop()\n\tdefer db.Close()\n\n\tfor n := 0; n < amount; n++ {\n\t\tif _, err := db.Add(ctx, []byte(fmt.Sprintf(\"%d\", n))); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "orbitdb_signed_entry_accesscontroller.go",
    "content": "package weshnet\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"sync\"\n\n\tcid \"github.com/ipfs/go-cid\"\n\tmh \"github.com/multiformats/go-multihash\"\n\t\"github.com/pkg/errors\"\n\t\"go.uber.org/zap\"\n\n\tlogac \"berty.tech/go-ipfs-log/accesscontroller\"\n\t\"berty.tech/go-ipfs-log/identityprovider\"\n\t\"berty.tech/go-orbit-db/accesscontroller\"\n\t\"berty.tech/go-orbit-db/iface\"\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n)\n\ntype simpleAccessController struct {\n\tallowedKeys map[string][]string\n\tlogger      *zap.Logger\n\tlock        sync.RWMutex\n}\n\nfunc (o *simpleAccessController) SetLogger(logger *zap.Logger) {\n\to.lock.Lock()\n\tdefer o.lock.Unlock()\n\n\to.logger = logger\n}\n\nfunc (o *simpleAccessController) Logger() *zap.Logger {\n\to.lock.RLock()\n\tdefer o.lock.RUnlock()\n\n\treturn o.logger\n}\n\n//nolint:revive\nfunc (o *simpleAccessController) Grant(ctx context.Context, capability string, keyID string) error {\n\treturn nil\n}\n\n//nolint:revive\nfunc (o *simpleAccessController) Revoke(ctx context.Context, capability string, keyID string) error {\n\treturn nil\n}\n\n//nolint:revive\nfunc (o *simpleAccessController) Load(ctx context.Context, address string) error {\n\treturn nil\n}\n\nfunc simpleAccessControllerCID(allowedKeys map[string][]string) (cid.Cid, error) {\n\td, err := json.Marshal(allowedKeys)\n\tif err != nil {\n\t\treturn cid.Undef, errcode.ErrCode_ErrInvalidInput.Wrap(err)\n\t}\n\n\tc, err := cid.Prefix{\n\t\tVersion:  1,\n\t\tCodec:    cid.Raw,\n\t\tMhType:   mh.SHA2_256,\n\t\tMhLength: -1,\n\t}.Sum(d)\n\tif err != nil {\n\t\treturn cid.Undef, errcode.ErrCode_ErrInvalidInput.Wrap(err)\n\t}\n\n\treturn c, nil\n}\n\nfunc (o *simpleAccessController) Save(context.Context) (accesscontroller.ManifestParams, error) {\n\tc, err := simpleAccessControllerCID(o.allowedKeys)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(err)\n\t}\n\n\treturn accesscontroller.NewManifestParams(c, true, \"simple\"), nil\n}\n\nfunc (o *simpleAccessController) Close() error {\n\treturn nil\n}\n\nfunc (o *simpleAccessController) Type() string {\n\treturn \"bertysimple\"\n}\n\nfunc (o *simpleAccessController) GetAuthorizedByRole(role string) ([]string, error) {\n\treturn o.allowedKeys[role], nil\n}\n\nfunc (o *simpleAccessController) CanAppend(e logac.LogEntry, _ identityprovider.Interface, _ accesscontroller.CanAppendAdditionalContext) error {\n\tfor _, id := range o.allowedKeys[\"write\"] {\n\t\tif e.GetIdentity().ID == id || id == \"*\" {\n\t\t\treturn nil\n\t\t}\n\t}\n\n\treturn errors.New(\"not allowed to write entry\")\n}\n\n// NewSimpleAccessController Returns a non configurable access controller\nfunc NewSimpleAccessController(_ context.Context, _ iface.BaseOrbitDB, params accesscontroller.ManifestParams, options ...accesscontroller.Option) (accesscontroller.Interface, error) {\n\tif params == nil {\n\t\treturn &simpleAccessController{}, errors.New(\"an options object is required\")\n\t}\n\n\tac := &simpleAccessController{\n\t\tallowedKeys: params.GetAllAccess(),\n\t}\n\n\tfor _, o := range options {\n\t\to(ac)\n\t}\n\n\treturn ac, nil\n}\n\nvar _ accesscontroller.Interface = &simpleAccessController{}\n"
  },
  {
    "path": "orbitdb_signed_entry_identity_provider.go",
    "content": "package weshnet\n\nimport (\n\t\"context\"\n\n\t\"github.com/libp2p/go-libp2p/core/crypto\"\n\n\t\"berty.tech/go-ipfs-log/identityprovider\"\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n)\n\nconst (\n\tidentityGroupIDKey = \"group_id\"\n\tstoreTypeKey       = \"store_type\"\n\tidentityType       = \"betry_group_entry\"\n)\n\ntype bertySignedIdentityProvider struct {\n\tkeyStore *BertySignedKeyStore\n}\n\nfunc (b *bertySignedIdentityProvider) UnmarshalPublicKey(data []byte) (crypto.PubKey, error) {\n\treturn crypto.UnmarshalPublicKey(data)\n}\n\nfunc (b *bertySignedIdentityProvider) GetID(_ context.Context, opts *identityprovider.CreateIdentityOptions) (string, error) {\n\treturn opts.ID, nil\n}\n\n//nolint:revive\nfunc (b *bertySignedIdentityProvider) SignIdentity(ctx context.Context, data []byte, id string) ([]byte, error) {\n\treturn nil, nil\n}\n\nfunc (b *bertySignedIdentityProvider) GetType() string {\n\treturn identityType\n}\n\nfunc (b *bertySignedIdentityProvider) VerifyIdentity(*identityprovider.Identity) error {\n\treturn nil\n}\n\nfunc (b *bertySignedIdentityProvider) Sign(ctx context.Context, identity *identityprovider.Identity, bytes []byte) ([]byte, error) {\n\tkey, err := b.keyStore.GetKey(ctx, identity.ID)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_TODO.Wrap(err)\n\t}\n\n\tsig, err := key.Sign(bytes)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_TODO.Wrap(err)\n\t}\n\n\treturn sig, nil\n}\n\nfunc (b *bertySignedIdentityProvider) signID(ctx context.Context, id string) (crypto.PubKey, []byte, error) {\n\tprivKey, err := b.keyStore.GetKey(ctx, id)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tidSignature, err := b.keyStore.Sign(privKey, []byte(id))\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\treturn privKey.GetPublic(), idSignature, nil\n}\n\nfunc (b *bertySignedIdentityProvider) createIdentity(ctx context.Context, options *identityprovider.CreateIdentityOptions) (*identityprovider.Identity, error) {\n\tid, err := b.GetID(ctx, options)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tpublicKey, idSignature, err := b.signID(ctx, id)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tpublicKeyRaw, err := publicKey.Raw()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tpublicKeyBytes, err := crypto.MarshalPublicKey(publicKey)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tpubKeyIDSignature, err := b.SignIdentity(ctx, append(publicKeyRaw, idSignature...), options.ID)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &identityprovider.Identity{\n\t\tID:        id,\n\t\tPublicKey: publicKeyBytes,\n\t\tSignatures: &identityprovider.IdentitySignature{\n\t\t\tID:        idSignature,\n\t\t\tPublicKey: pubKeyIDSignature,\n\t\t},\n\t\tType:     b.GetType(),\n\t\tProvider: b,\n\t}, nil\n}\n\nvar _ identityprovider.Interface = (*bertySignedIdentityProvider)(nil)\n"
  },
  {
    "path": "orbitdb_signed_entry_keystore.go",
    "content": "package weshnet\n\nimport (\n\t\"context\"\n\t\"encoding/hex\"\n\t\"sync\"\n\n\t\"github.com/libp2p/go-libp2p/core/crypto\"\n\n\t\"berty.tech/go-ipfs-log/keystore\"\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n)\n\ntype BertySignedKeyStore struct {\n\tsync.Map\n}\n\nfunc (s *BertySignedKeyStore) SetKey(pk crypto.PrivKey) error {\n\tpubKeyBytes, err := pk.GetPublic().Raw()\n\tif err != nil {\n\t\treturn errcode.ErrCode_TODO.Wrap(err)\n\t}\n\n\tkeyID := hex.EncodeToString(pubKeyBytes)\n\n\ts.Store(keyID, pk)\n\n\treturn nil\n}\n\nfunc (s *BertySignedKeyStore) HasKey(_ context.Context, id string) (bool, error) {\n\t_, ok := s.Load(id)\n\n\treturn ok, nil\n}\n\nfunc (s *BertySignedKeyStore) CreateKey(ctx context.Context, id string) (crypto.PrivKey, error) {\n\treturn s.GetKey(ctx, id)\n}\n\nfunc (s *BertySignedKeyStore) GetKey(_ context.Context, id string) (crypto.PrivKey, error) {\n\tif privKey, ok := s.Load(id); ok {\n\t\tif pk, ok := privKey.(crypto.PrivKey); ok {\n\t\t\treturn pk, nil\n\t\t}\n\t}\n\n\treturn nil, errcode.ErrCode_ErrGroupMemberUnknownGroupID\n}\n\nfunc (s *BertySignedKeyStore) Sign(privKey crypto.PrivKey, bytes []byte) ([]byte, error) {\n\treturn privKey.Sign(bytes)\n}\n\nfunc (s *BertySignedKeyStore) Verify(signature []byte, publicKey crypto.PubKey, data []byte) error {\n\tok, err := publicKey.Verify(data, signature)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif !ok {\n\t\treturn errcode.ErrCode_ErrGroupMemberLogEventSignature\n\t}\n\n\treturn nil\n}\n\nfunc (s *BertySignedKeyStore) getIdentityProvider() *bertySignedIdentityProvider {\n\treturn &bertySignedIdentityProvider{\n\t\tkeyStore: s,\n\t}\n}\n\nvar _ keystore.Interface = (*BertySignedKeyStore)(nil)\n"
  },
  {
    "path": "orbitdb_test.go",
    "content": "package weshnet\n\nimport (\n\t\"context\"\n\t\"encoding/hex\"\n\t\"os\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\tdatastore \"github.com/ipfs/go-datastore\"\n\tsync_ds \"github.com/ipfs/go-datastore/sync\"\n\tmocknet \"github.com/libp2p/go-libp2p/p2p/net/mock\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/zap\"\n\n\t\"berty.tech/weshnet/v2/internal/datastoreutil\"\n\t\"berty.tech/weshnet/v2/pkg/ipfsutil\"\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n\t\"berty.tech/weshnet/v2/pkg/testutil\"\n\t\"berty.tech/weshnet/v2/pkg/tinder\"\n)\n\nfunc TestDifferentStores(t *testing.T) {\n\ttestutil.FilterSpeed(t, testutil.Slow)\n\tlogger, cleanup := testutil.Logger(t)\n\tdefer cleanup()\n\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\tmn := mocknet.New()\n\tdefer mn.Close()\n\n\tipfsOpts := &ipfsutil.TestingAPIOpts{\n\t\tLogger:          logger,\n\t\tMocknet:         mn,\n\t\tDiscoveryServer: tinder.NewMockDriverServer(),\n\t}\n\n\tpathBase, err := os.MkdirTemp(\"\", \"odb_manyaddstest\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\trequire.NoError(t, mn.ConnectAllButSelf())\n\n\tbaseDS, err := GetRootDatastoreForPath(pathBase, nil, nil, zap.NewNop())\n\trequire.NoError(t, err)\n\n\tbaseDS = sync_ds.MutexWrap(baseDS)\n\n\tdefer testutil.Close(t, baseDS)\n\n\tapi1 := ipfsutil.TestingCoreAPIUsingMockNet(ctx, t, ipfsOpts)\n\n\todb1 := NewTestOrbitDB(ctx, t, logger, api1, datastoreutil.NewNamespacedDatastore(baseDS, datastore.NewKey(\"peer1\")))\n\tdefer testutil.Close(t, odb1)\n\n\tapi2 := ipfsutil.TestingCoreAPIUsingMockNet(ctx, t, ipfsOpts)\n\n\todb2 := NewTestOrbitDB(ctx, t, logger, api2, datastoreutil.NewNamespacedDatastore(baseDS, datastore.NewKey(\"peer2\")))\n\tdefer testutil.Close(t, odb2)\n\n\terr = mn.LinkAll()\n\trequire.NoError(t, err)\n\n\terr = mn.ConnectAllButSelf()\n\trequire.NoError(t, err)\n\n\tgA, _, err := NewGroupMultiMember()\n\trequire.NoError(t, err)\n\n\tgB, _, err := NewGroupMultiMember()\n\trequire.NoError(t, err)\n\n\tassert.NotEqual(t, gA.PublicKey, gB.PublicKey)\n\n\tg1a, err := odb1.OpenGroup(ctx, gA, nil)\n\trequire.NoError(t, err)\n\tdefer g1a.Close()\n\n\tg2a, err := odb2.OpenGroup(ctx, gA, nil)\n\trequire.NoError(t, err)\n\tdefer g2a.Close()\n\n\tg1b, err := odb1.OpenGroup(ctx, gB, nil)\n\trequire.NoError(t, err)\n\tdefer g1b.Close()\n\n\tg2b, err := odb2.OpenGroup(ctx, gB, nil)\n\trequire.NoError(t, err)\n\tdefer g2b.Close()\n\n\tassert.Equal(t, g1a.MetadataStore().Address().String(), g2a.MetadataStore().Address().String())\n\tassert.Equal(t, g1b.MetadataStore().Address().String(), g2b.MetadataStore().Address().String())\n\tassert.NotEqual(t, g1a.MetadataStore().Address().String(), g1a.MessageStore().Address().String())\n\tassert.NotEqual(t, g1a.MetadataStore().Address().String(), g1b.MetadataStore().Address().String())\n\n\tauthorized1, err := g1a.MetadataStore().AccessController().GetAuthorizedByRole(\"write\")\n\trequire.NoError(t, err)\n\n\tauthorized2, err := g1a.MetadataStore().AccessController().GetAuthorizedByRole(\"write\")\n\trequire.NoError(t, err)\n\n\tassert.Equal(t, strings.Join(authorized1, \",\"), strings.Join(authorized2, \",\"))\n\n\tpk1, err := g1a.MetadataStore().Identity().GetPublicKey()\n\trequire.NoError(t, err)\n\n\tpk2, err := g2a.MetadataStore().Identity().GetPublicKey()\n\trequire.NoError(t, err)\n\n\trequire.True(t, pk1.Equals(pk2))\n\n\trawPK, err := pk1.Raw()\n\trequire.NoError(t, err)\n\n\trequire.Equal(t, hex.EncodeToString(rawPK), authorized1[0])\n\n\t_, err = g1a.MetadataStore().SendAppMetadata(ctx, []byte(\"From 1 - 1\"))\n\trequire.NoError(t, err)\n\n\t_, err = g2a.MetadataStore().SendAppMetadata(ctx, []byte(\"From 2 - 1\"))\n\trequire.NoError(t, err)\n\n\t_, err = g1b.MetadataStore().SendAppMetadata(ctx, []byte(\"From 1 - 2\"))\n\trequire.NoError(t, err)\n\n\t_, err = g2b.MetadataStore().SendAppMetadata(ctx, []byte(\"From 2 - 2\"))\n\trequire.NoError(t, err)\n\n\t_, err = g1b.MetadataStore().SendAppMetadata(ctx, []byte(\"From 1 - 3\"))\n\trequire.NoError(t, err)\n\n\t_, err = g2b.MetadataStore().SendAppMetadata(ctx, []byte(\"From 2 - 3\"))\n\trequire.NoError(t, err)\n\n\t{\n\t\tvar err error\n\t\tvar cc <-chan *protocoltypes.GroupMetadataEvent\n\t\tvar ops []*protocoltypes.GroupMetadataPayloadSent\n\n\t\tassert.Eventually(t, func() bool {\n\t\t\tcc, err = g1a.MetadataStore().ListEvents(ctx, nil, nil, false)\n\t\t\tops = testutil.TestFilterGroupMetadataPayloadSent(t, cc)\n\t\t\treturn len(ops) == 2\n\t\t}, time.Second*2, time.Millisecond*100, \"have: %d, want: %d\", len(ops), 2)\n\t\trequire.NoError(t, err)\n\n\t\tassert.Eventually(t, func() bool {\n\t\t\tcc, err = g2a.MetadataStore().ListEvents(ctx, nil, nil, false)\n\t\t\tops = testutil.TestFilterGroupMetadataPayloadSent(t, cc)\n\t\t\treturn len(ops) == 2\n\t\t}, time.Second*2, time.Millisecond*100, \"have: %d, want: %d\", len(ops), 2)\n\t\trequire.NoError(t, err)\n\n\t\tassert.Eventually(t, func() bool {\n\t\t\tcc, err = g1b.MetadataStore().ListEvents(ctx, nil, nil, false)\n\t\t\tops = testutil.TestFilterGroupMetadataPayloadSent(t, cc)\n\t\t\treturn len(ops) == 4\n\t\t}, time.Second*2, time.Millisecond*100, \"have: %d, want: %d\", len(ops), 5)\n\t\trequire.NoError(t, err)\n\n\t\tassert.Eventually(t, func() bool {\n\t\t\tcc, err = g2b.MetadataStore().ListEvents(ctx, nil, nil, false)\n\t\t\tops = testutil.TestFilterGroupMetadataPayloadSent(t, cc)\n\t\t\treturn len(ops) == 4\n\t\t}, time.Second*2, time.Millisecond*100, \"have: %d, want: %d\", len(ops), 4)\n\t\trequire.NoError(t, err)\n\t}\n}\n"
  },
  {
    "path": "orbitdb_utils_test.go",
    "content": "package weshnet\n\nimport (\n\t\"context\"\n\t\"sync\"\n\t\"testing\"\n\n\t\"github.com/libp2p/go-libp2p/core/crypto\"\n\t\"github.com/stretchr/testify/require\"\n\t\"google.golang.org/protobuf/proto\"\n\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n)\n\nfunc inviteAllPeersToGroup(ctx context.Context, t *testing.T, peers []*mockedPeer, groupSK crypto.PrivKey) {\n\tt.Helper()\n\n\twg := sync.WaitGroup{}\n\twg.Add(len(peers))\n\n\terrChan := make(chan error, len(peers))\n\n\tfor i, p := range peers {\n\t\tsub, err := p.GC.MetadataStore().EventBus().Subscribe(new(*protocoltypes.GroupMetadataEvent))\n\t\trequire.NoError(t, err)\n\t\tgo func(p *mockedPeer, peerIndex int) {\n\t\t\tdefer sub.Close()\n\t\t\tdefer wg.Done()\n\n\t\t\teventReceived := 0\n\n\t\t\tfor e := range sub.Out() {\n\t\t\t\tevt := e.(*protocoltypes.GroupMetadataEvent)\n\t\t\t\tif evt.Metadata.EventType != protocoltypes.EventType_EventTypeGroupMemberDeviceAdded {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tmemdev := &protocoltypes.GroupMemberDeviceAdded{}\n\t\t\t\tif err := proto.Unmarshal(evt.Event, memdev); err != nil {\n\t\t\t\t\terrChan <- err\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\teventReceived++\n\t\t\t\tif eventReceived == len(peers) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t}(p, i)\n\t}\n\n\tfor i, p := range peers {\n\t\t_, err := p.GC.MetadataStore().AddDeviceToGroup(ctx)\n\t\trequire.NoError(t, err)\n\n\t\tif i == 0 {\n\t\t\t_, err := p.GC.MetadataStore().ClaimGroupOwnership(ctx, groupSK)\n\t\t\trequire.NoError(t, err)\n\t\t}\n\t}\n\n\t// Wait for all events to be received in all peers's member log (or timeout)\n\twg.Wait()\n\tclose(errChan)\n\n\tfor err := range errChan {\n\t\tt.Fatal(err)\n\t}\n}\n\nfunc waitForBertyEventType(ctx context.Context, t *testing.T, ms *MetadataStore, eventType protocoltypes.EventType, eventCount int, done chan struct{}) {\n\tt.Helper()\n\n\tctx, cancel := context.WithCancel(ctx)\n\tdefer cancel()\n\n\thandledEvents := map[string]struct{}{}\n\n\tsub, err := ms.EventBus().Subscribe(new(*protocoltypes.GroupMetadataEvent))\n\trequire.NoError(t, err)\n\tdefer sub.Close()\n\n\tfor {\n\t\tvar e any\n\n\t\tselect {\n\t\tcase e = <-sub.Out():\n\t\tcase <-ctx.Done():\n\t\t\treturn\n\t\t}\n\n\t\tswitch evt := e.(type) {\n\t\tcase *protocoltypes.GroupMetadataEvent:\n\t\t\tif evt.Metadata.EventType != eventType {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\teID := string(evt.EventContext.Id)\n\n\t\t\tif _, ok := handledEvents[eID]; ok {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\thandledEvents[eID] = struct{}{}\n\n\t\t\te := &protocoltypes.GroupDeviceChainKeyAdded{}\n\t\t\tif err := proto.Unmarshal(evt.Event, e); err != nil {\n\t\t\t\tt.Fatalf(\" err: %+v\\n\", err.Error())\n\t\t\t}\n\n\t\t\t// fmt.Println(string(e.DevicePK), string(e.DestMemberPK))\n\n\t\t\teventCount--\n\t\t\tif eventCount == 0 {\n\t\t\t\tdone <- struct{}{}\n\t\t\t} else {\n\t\t\t\t// fmt.Println(eventCount, \"more to go\")\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "pkg/androidnearby/bridge_android.go",
    "content": "//go:build android && !noproximitytransport\n\npackage androidnearby\n\nimport (\n\t\"go.uber.org/zap\"\n\n\tproximity \"berty.tech/weshnet/v2/pkg/proximitytransport\"\n)\n\n// Supported is used by main package as default value for enable this driver.\n// While UI actually enable or not the Java Android Nearby driver.\n// TODO: remove this when UI will be able to handle this for the first App launching.\nconst Supported = true\n\n// Noop implementation for Android\n// Real driver is given from Java directly here: berty/js/android/app/src/main/java/tech/berty/gobridge/nearby\nfunc NewDriver(logger *zap.Logger) proximity.ProximityDriver {\n\tlogger = logger.Named(\"Nearby\")\n\tlogger.Info(\"NewDriver(): Java driver not found\")\n\n\treturn proximity.NewNoopProximityDriver(ProtocolCode, ProtocolName, DefaultAddr)\n}\n"
  },
  {
    "path": "pkg/androidnearby/bridge_unsupported.go",
    "content": "//go:build !android || noproximitytransport\n\npackage androidnearby\n\nimport (\n\t\"go.uber.org/zap\"\n\n\tproximity \"berty.tech/weshnet/v2/pkg/proximitytransport\"\n)\n\nconst Supported = false\n\n// Noop implementation for platform that are not Darwin\n\nfunc NewDriver(logger *zap.Logger) proximity.ProximityDriver {\n\tlogger = logger.Named(\"Nearby\")\n\tlogger.Info(\"NewDriver(): incompatible system\")\n\n\treturn proximity.NewNoopProximityDriver(ProtocolCode, ProtocolName, DefaultAddr)\n}\n"
  },
  {
    "path": "pkg/androidnearby/const.go",
    "content": "package androidnearby\n\nconst (\n\tDefaultAddr  = \"/nearby/Qmeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\"\n\tProtocolCode = 0x0044\n\tProtocolName = \"nearby\"\n)\n"
  },
  {
    "path": "pkg/androidnearby/example_test.go",
    "content": "package androidnearby_test\n"
  },
  {
    "path": "pkg/androidnearby/init.go",
    "content": "package androidnearby\n\nimport (\n\tma \"github.com/multiformats/go-multiaddr\"\n)\n\n// Add MC to the list of libp2p's multiaddr protocols\n// FIXME: remove this init\nfunc init() { // nolint:gochecknoinits\n\terr := ma.AddProtocol(newProtocol())\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n"
  },
  {
    "path": "pkg/androidnearby/multiaddr.go",
    "content": "package androidnearby\n\nimport (\n\tpeer \"github.com/libp2p/go-libp2p/core/peer\"\n\tma \"github.com/multiformats/go-multiaddr\"\n)\n\nfunc newProtocol() ma.Protocol {\n\ttranscoderMC := ma.NewTranscoderFromFunctions(mcStB, mcBtS, mcVal)\n\treturn ma.Protocol{\n\t\tName:       ProtocolName,\n\t\tCode:       ProtocolCode,\n\t\tVCode:      ma.CodeToVarint(ProtocolCode),\n\t\tSize:       -1,\n\t\tPath:       false,\n\t\tTranscoder: transcoderMC,\n\t}\n}\n\nfunc mcStB(s string) ([]byte, error) {\n\t_, err := peer.Decode(s)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn []byte(s), nil\n}\n\nfunc mcBtS(b []byte) (string, error) {\n\t_, err := peer.Decode(string(b))\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn string(b), nil\n}\n\nfunc mcVal(b []byte) error {\n\t_, err := peer.Decode(string(b))\n\treturn err\n}\n"
  },
  {
    "path": "pkg/bertyvcissuer/client.go",
    "content": "package bertyvcissuer\n\nimport (\n\t\"context\"\n\t\"crypto\"\n\tcrand \"crypto/rand\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"time\"\n\n\t\"github.com/hyperledger/aries-framework-go/pkg/doc/verifiable\"\n\t\"github.com/piprate/json-gold/ld\"\n\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n\t\"berty.tech/weshnet/v2/pkg/verifiablecredstypes\"\n)\n\nconst DefaultRedirectURI = \"berty://vc\"\n\ntype Client struct {\n\tserverRoot  string\n\tredirectURI string\n\thttpClient  *http.Client\n\tstate       string\n\tbertyURL    string\n}\n\nfunc NewClient(serverRoot string) *Client {\n\treturn &Client{\n\t\tserverRoot:  serverRoot,\n\t\tredirectURI: DefaultRedirectURI,\n\t\thttpClient:  http.DefaultClient,\n\t}\n}\n\nfunc (c *Client) Init(ctx context.Context, bertyURL string, accountPriv crypto.Signer) (string, error) {\n\tc.state = base64.RawURLEncoding.EncodeToString([]byte(time.Now().String()))\n\tc.bertyURL = bertyURL\n\n\treq, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf(\"%s/%s?%s=%s&%s=%s&%s=%s\", c.serverRoot, PathChallenge, ParamBertyID, url.QueryEscape(bertyURL), ParamRedirectURI, url.QueryEscape(c.redirectURI), ParamState, url.QueryEscape(c.state)), nil)\n\tif err != nil {\n\t\treturn \"\", errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\n\tres, err := c.httpClient.Do(req)\n\tif err != nil {\n\t\treturn \"\", errcode.ErrCode_ErrStreamRead.Wrap(err)\n\t}\n\n\tresBytes, err := io.ReadAll(res.Body)\n\tif err != nil {\n\t\treturn \"\", errcode.ErrCode_ErrStreamRead.Wrap(err)\n\t}\n\n\t_ = res.Body.Close()\n\n\tif res.StatusCode != http.StatusOK {\n\t\treturn \"\", errcode.ErrCode_ErrInternal.Wrap(fmt.Errorf(string(resBytes)))\n\t}\n\n\tchallengeStruct := &verifiablecredstypes.AccountCryptoChallenge{}\n\terr = json.Unmarshal(resBytes, challengeStruct)\n\tif err != nil {\n\t\treturn \"\", errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\tchallenge, err := base64.URLEncoding.DecodeString(challengeStruct.Challenge)\n\tif err != nil {\n\t\treturn \"\", errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\tchallengeSig, err := accountPriv.Sign(crand.Reader, challenge, crypto.Hash(0))\n\tif err != nil {\n\t\treturn \"\", errcode.ErrCode_ErrCryptoSignature.Wrap(err)\n\t}\n\n\treturn fmt.Sprintf(\"%s/%s?&%s=%s&%s=%s\", c.serverRoot, PathAuthenticate, ParamChallenge, challengeStruct.Challenge, ParamChallengeSig, base64.URLEncoding.EncodeToString(challengeSig)), nil\n}\n\nfunc (c *Client) Complete(uri string) (string, string, *verifiable.Credential, error) {\n\tparsedURI, err := url.Parse(uri)\n\tif err != nil {\n\t\treturn \"\", \"\", nil, errcode.ErrCode_ErrInvalidInput.Wrap(err)\n\t}\n\n\tif parsedURI.Query().Get(ParamState) != c.state {\n\t\treturn \"\", \"\", nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"unexpected state value\"))\n\t}\n\n\tcredentialsStr := parsedURI.Query().Get(ParamCredentials)\n\tif len(credentialsStr) == 0 {\n\t\treturn \"\", \"\", nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"missing credentials value\"))\n\t}\n\n\tcredentials, err := base64.StdEncoding.DecodeString(credentialsStr)\n\tif err != nil {\n\t\treturn \"\", \"\", nil, errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\tparsedCredential, err := verifiable.ParseCredential(\n\t\tcredentials,\n\t\tverifiable.WithPublicKeyFetcher(EmbeddedPublicKeyFetcher),\n\t\tverifiable.WithJSONLDDocumentLoader(ld.NewDefaultDocumentLoader(http.DefaultClient)),\n\t)\n\tif err != nil {\n\t\treturn \"\", \"\", nil, errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\tif c.bertyURL != parsedCredential.ID {\n\t\treturn \"\", \"\", nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"credential is not delivered for the current berty url (%s != %s)\", c.bertyURL, parsedCredential.ID))\n\t}\n\n\tidentifier, err := ExtractSubjectFromVC(parsedCredential)\n\tif err != nil {\n\t\treturn \"\", \"\", nil, errcode.ErrCode_ErrInvalidInput.Wrap(err)\n\t}\n\n\treturn string(credentials), identifier, parsedCredential, nil\n}\n\nfunc ExtractSubjectFromVC(credential *verifiable.Credential) (string, error) {\n\tif credential.Subject == nil {\n\t\treturn \"\", errcode.ErrCode_ErrNotFound\n\t}\n\n\tif subjectList, ok := credential.Subject.([]verifiable.Subject); ok {\n\t\tif len(subjectList) == 0 {\n\t\t\treturn \"\", errcode.ErrCode_ErrNotFound\n\t\t}\n\n\t\treturn subjectList[0].ID, nil\n\t} else if subject, ok := credential.Subject.(string); ok && subject != \"\" {\n\t\treturn subject, nil\n\t}\n\n\treturn \"\", errcode.ErrCode_ErrNotFound\n}\n"
  },
  {
    "path": "pkg/bertyvcissuer/urls.go",
    "content": "package bertyvcissuer\n\nimport (\n\t\"encoding/base64\"\n\t\"fmt\"\n)\n\nconst (\n\tPathChallenge    = \"/challenge\"\n\tPathAuthenticate = \"/authenticate\"\n\tPathProof        = \"/proof\"\n\n\tParamBertyID      = \"berty_id\"\n\tParamState        = \"state\"\n\tParamRedirectURI  = \"redirect_uri\"\n\tParamChallenge    = \"challenge\"\n\tParamChallengeSig = \"challenge_sig\"\n\tParamCode         = \"code\"\n\tParamContext      = \"context\"\n\tParamCredentials  = \"credentials\"\n\tParamIdentifier   = \"identifier\"\n)\n\nfunc MakeAuthenticateURL(serverBaseRoot, flowCtxStr string) string {\n\treturn fmt.Sprintf(\"%s%s?%s=%s\", serverBaseRoot, PathAuthenticate, ParamContext, flowCtxStr)\n}\n\nfunc MakeProofURL(serverBaseRoot, flowCtxStr string) string {\n\treturn fmt.Sprintf(\"%s%s?%s=%s\", serverBaseRoot, PathProof, ParamContext, flowCtxStr)\n}\n\nfunc MakeRedirectSuccessURI(redirectURI, state string, credentials []byte) string {\n\treturn fmt.Sprintf(\"%s%s?%s=%s&%s=%s\", redirectURI, PathProof, ParamState, state, ParamCredentials, base64.URLEncoding.EncodeToString(credentials))\n}\n"
  },
  {
    "path": "pkg/bertyvcissuer/verifiable_public_key_fetcher.go",
    "content": "package bertyvcissuer\n\nimport (\n\t\"crypto/ed25519\"\n\t\"fmt\"\n\t\"slices\"\n\t\"strings\"\n\n\t\"github.com/hyperledger/aries-framework-go/pkg/doc/signature/verifier\"\n\t\"github.com/hyperledger/aries-framework-go/pkg/kms\"\n\t\"github.com/multiformats/go-multibase\"\n\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n)\n\nfunc embeddedPublicKeyFetcher(issuerID string, allowList []string) (*verifier.PublicKey, error) {\n\tif !strings.HasPrefix(issuerID, \"did:key:z6Mk\") {\n\t\treturn nil, fmt.Errorf(\"unexpected key format\")\n\t}\n\n\tif len(allowList) > 0 {\n\t\tfound := slices.Contains(allowList, issuerID)\n\n\t\tif !found {\n\t\t\treturn nil, errcode.ErrCode_ErrServicesDirectoryInvalidVerifiedCredentialID.Wrap(fmt.Errorf(\"issuer is not allowed\"))\n\t\t}\n\t}\n\n\t_, rawData, err := multibase.Decode(issuerID[8:])\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif len(rawData) != ed25519.PublicKeySize+2 {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput\n\t}\n\n\treturn &verifier.PublicKey{\n\t\tType:  kms.ED25519,\n\t\tValue: rawData[2:],\n\t\tJWK:   nil,\n\t}, nil\n}\n\n// nolint:revive\nfunc EmbeddedPublicKeyFetcher(issuerID, keyID string) (*verifier.PublicKey, error) {\n\treturn embeddedPublicKeyFetcher(issuerID, nil)\n}\n\n// nolint:revive\nfunc EmbeddedPublicKeyFetcherAllowList(allowList []string) func(issuerID, keyID string) (*verifier.PublicKey, error) {\n\treturn func(issuerID, keyID string) (*verifier.PublicKey, error) {\n\t\treturn embeddedPublicKeyFetcher(issuerID, allowList)\n\t}\n}\n"
  },
  {
    "path": "pkg/ble-driver/BertyDevice_darwin.h",
    "content": "//\n//  BertyDevice.h\n//  ble\n//\n//  Created by sacha on 03/06/2019.\n//  Copyright © 2019 berty. All rights reserved.\n//\n\n#import <Foundation/Foundation.h>\n#import <CoreBluetooth/CoreBluetooth.h>\n\n#import \"ConnectedPeer.h\"\n#import \"CircularQueue.h\"\n#import \"BleQueue.h\"\n#import \"Logger.h\"\n#import \"CountDownLatch_darwin.h\"\n\nNS_ASSUME_NONNULL_BEGIN\n\n@class BleManager;\n\ntypedef void (^BertyDeviceConnectCallbackBlockType)(BertyDevice * __nullable, NSError * __nullable);\ntypedef void (^BertyDeviceServiceCallbackBlockType)(NSArray * __nullable, NSError * __nullable);\ntypedef void (^BertyDeviceWriteCallbackBlockType)(NSError * __nullable);\n#define _BERTY_ON_D_THREAD(block) dispatch_async(self.dQueue, block)\n#define _BERTY_ON_W_THREAD(block) dispatch_async(self.writeQueue, block)\n\n@interface BertyDevice : NSObject <CBPeripheralDelegate, NSStreamDelegate>\n\n@property (nonatomic, strong, nonnull) Logger *logger;\n@property (nonatomic, strong, nonnull) NSString *name;\n@property (nonatomic, strong, nonnull) NSDictionary *serviceDict;\n@property (nonatomic, strong, nullable) CBPeripheral *peripheral;\n@property (nonatomic, strong, nonnull) NSString *serverSideIdentifier;\n@property (nonatomic, strong, nonnull) NSString *clientSideIdentifier;\n@property (nonatomic, assign, nullable) BleManager *manager;\n@property (nonatomic, strong, nonnull) BleQueue *connectionQ;\n@property (nonatomic, strong, nonnull) BleQueue *writeQ;\n@property (nonatomic, strong, nonnull) BleQueue *readQ;\n@property (nonatomic, strong, nullable) NSObject *writerLatch;\n@property (nonatomic, strong, nullable) CBCharacteristic *peerIDCharacteristic;\n@property (nonatomic, strong, nullable) CBCharacteristic *writerCharacteristic;\n@property (nonatomic, strong, nonnull) NSDictionary* characteristicHandlers;\n@property (nonatomic, strong, nonnull) NSDictionary* characteristicData;\n@property (nonatomic, strong, nullable) NSData *remainingData;\n@property (nonatomic, strong, nullable) NSString *remotePeerID;\n@property (readwrite) int psm;\n@property (nonatomic, strong, nullable) ConnectedPeer *peer;\n@property (nonatomic, strong, nonnull) CBCentral *cbCentral;\n@property (nonatomic, strong, nonnull) CircularQueue *dataCache;\n@property (readwrite) BOOL isDisconnecting;\n\n- (instancetype __nullable)initWithIdentifier:(NSString *__nonnull)identifier logger:(Logger *__nonnull)logger central:(BleManager *__nonnull)manager asClient:(BOOL)client;\n- (instancetype __nullable)initWithPeripheral:(CBPeripheral *__nonnull)peripheral logger:(Logger *__nonnull)logger central:(BleManager *__nonnull)manager withName:(NSString *__nonnull)name;\n- (void)closeBertyDevice;\n- (BOOL)writeToCharacteristic:(NSData *__nonnull)data forCharacteristic:(CBCharacteristic *__nonnull)characteristic withEOD:(BOOL)eod;\n- (void)handshake;\n- (void)handleConnect:(NSError * __nullable)error;\n- (void)connectWithOptions:(NSDictionary * __nullable)options;\n- (NSString *__nonnull)getIdentifier;\n- (void)flushCache;\n\n@end\n\nAPI_AVAILABLE(ios(11.0))\n@interface BertyDevice()\n\n@property (strong, nullable) NSThread *l2capThread;\n@property (strong, nullable) CBL2CAPChannel *l2capChannel;\n@property (strong, nullable) NSData *l2capWriteData;\n@property (readwrite) NSInteger l2capWriteIndex;\n@property (readwrite) BOOL useL2cap;\n@property (readwrite) BOOL l2capClientHandshakeRunning;\n@property (readwrite) BOOL l2capServerHandshakeRunning;\n@property (strong, nullable) CountDownLatch *l2capHandshakeLatch;\n@property (readwrite) BOOL l2capHandshakeStepStatus;\n@property (copy, nullable) dispatch_block_t l2capHandshakeBlock;\n@property (strong, nullable) NSMutableData *l2capHandshakeData;\n@property (strong, nullable) NSMutableData *l2capHandshakeRecvData;\n@property (readwrite) NSUInteger l2capHandshakeRecvDataLen;\n\n- (BOOL)l2capWrite:(NSData *__nonnull)data;\n\n@end\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "pkg/ble-driver/BertyDevice_darwin.m",
    "content": "// +build darwin,!noproximitytransport\n//\n//  BertyDevice.m\n//  ble\n//\n//  Created by sacha on 03/06/2019.\n//  Copyright © 2019 berty. All rights reserved.\n//\n\n#import \"BertyDevice_darwin.h\"\n#import \"BleManager_darwin.h\"\n\nextern unsigned short handlePeerFound(char *, char *);\nextern void receiveFromDevice(char *, void *, int);\n\nstatic NSString* const __nonnull EOD = @\"EOD\";\nstatic const int L2CAP_BUFFER = 4096;\nstatic const int L2CAP_HANDSHAKE_DATA = 1024;\n\nCBService *getService(NSArray *services, NSString *uuid) {\n    CBService *result = nil;\n\n    for (CBService *service in services) {\n        if ([service.UUID.UUIDString containsString:uuid] != NSNotFound) {\n            result = service;\n        }\n    }\n    return result;\n}\n\n@implementation BertyDevice\n\n- (instancetype)initWithPeripheral:(CBPeripheral *)peripheral logger:(Logger *__nonnull)logger\n                           central:(BleManager *)manager withName:(NSString *__nonnull)name {\n    self = [self initWithIdentifier:[peripheral.identifier UUIDString] logger:logger central:manager asClient:TRUE];\n\n    if (self) {\n        _peripheral = [peripheral retain];\n        _name = name;\n    }\n\n    return self;\n}\n\n- (instancetype)initWithIdentifier:(NSString *)identifier logger:(Logger *__nonnull)logger central:(BleManager *)manager asClient:(BOOL)client{\n    self = [super init];\n\n    if (self) {\n        if (client) {\n            _clientSideIdentifier = [identifier retain];\n        } else {\n            _serverSideIdentifier = [identifier retain];\n        }\n\n        _logger = [logger retain];\n        _peripheral = nil;\n        _manager = manager;\n        _remotePeerID = nil;\n        _psm = 0;\n\n        _connectionQ = [[BleQueue alloc] init: dispatch_get_main_queue() logger:logger];\n        _writeQ = [[BleQueue alloc] init: dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) logger:logger];\n        _readQ = [[BleQueue alloc] init: dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) logger:logger];\n\n        BOOL (^peerIDHandler)(NSData *data) = ^BOOL(NSData *data) {\n            return [self handlePeerID:data];\n        };\n\n        BOOL (^writeHandler)(NSData *data) = ^BOOL(NSData *data) {\n            return [self handleIncomingData:data];\n        };\n\n        _characteristicHandlers = [@{\n            [BleManager.writerUUID UUIDString]: [[writeHandler copy] autorelease],\n            [BleManager.peerUUID UUIDString]: [[peerIDHandler copy] autorelease],\n        } retain];\n\n        _characteristicData = [@{\n            [BleManager.writerUUID UUIDString]: [NSMutableData data],\n            [BleManager.peerUUID UUIDString]: [NSMutableData data],\n        } retain];\n\n        // put inside incoming message arrived before handshake is completed\n        _dataCache = [[CircularQueue alloc] initWithCapacity:10];\n\n        _writerLatch = [[NSObject alloc] init];\n    }\n\n    return self;\n}\n\n- (void)dealloc {\n    [_logger release];\n    [_clientSideIdentifier release];\n    [_serverSideIdentifier release];\n    [_peripheral release];\n    _manager = nil;\n    [_remotePeerID release];\n    [_connectionQ release];\n    [_writeQ release];\n    [_readQ release];\n    [_characteristicHandlers release];\n    [_characteristicData release];\n    [_dataCache release];\n    [_writerLatch release];\n\n    [super dealloc];\n}\n\n- (NSString *__nonnull)getIdentifier {\n    if (self.clientSideIdentifier != nil) {\n        return self.clientSideIdentifier;\n    }\n\n    return self.serverSideIdentifier;\n}\n\n- (void)closeL2cap {\n    [self.logger d:@\"closeL2cap: device=%@\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n\n    if (self.l2capChannel != nil) {\n        [self.l2capChannel.inputStream close];\n        [self.l2capChannel.outputStream close];\n\n        if (self.l2capThread != nil) {\n            [self.l2capThread cancel];\n            [self.l2capThread release];\n            self.l2capThread = nil;\n        }\n    }\n}\n\n- (void)closeBertyDevice {\n    @synchronized (self) {\n        [self.logger d:@\"closeBertyDevice: device=%@\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n\n        if (!self.isDisconnecting) {\n            self.isDisconnecting = TRUE;\n\n            [self.connectionQ clear];\n            [self.writeQ clear];\n            [self.readQ clear];\n\n            [self closeL2cap];\n            if (self.peer != nil) {\n                [self.manager.peerManager unregisterDevice:self];\n                self.peer = nil;\n            }\n        } else {\n            [self.logger d:@\"closeBertyDevice: device=%@ is already disconnecting\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n        }\n        self.isDisconnecting = FALSE;\n    }\n}\n\n- (BOOL)handlePeerID:(NSData *__nonnull)peerIDData {\n    if (self.peer != nil && [self.peer isConnected]) {\n        [self.logger e:@\"handlePeerID: device=%@: peer already connected\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n        return FALSE;\n    }\n\n    NSMutableData *tmpData = [self.characteristicData objectForKey:[BleManager.peerUUID UUIDString]];\n\n    if ([peerIDData isEqual:[EOD dataUsingEncoding:NSUTF8StringEncoding]]) {\n        // adding 0 byte\n        unsigned char zeroByte = 0;\n        @synchronized (tmpData) {\n            [tmpData appendBytes:&zeroByte length:1];\n        }\n\n        NSString *remotePeerID = [NSString stringWithUTF8String:[tmpData bytes]];\n        // reset tmpData\n        [tmpData setLength:0];\n        [self.logger d:@\"handlePeerID: device=%@: current peerID=%@, new peerID=%@\", [self.logger SensitiveNSObject:[self getIdentifier]], [self.logger SensitiveNSObject:self.remotePeerID], [self.logger SensitiveNSObject:remotePeerID]];\n        self.remotePeerID = remotePeerID;\n    } else {\n        @synchronized (tmpData) {\n            [self.logger d:@\"handlePeerID: device=%@: add to buffer data=%@\", [self.logger SensitiveNSObject:[self getIdentifier]], [self.logger SensitiveNSObject:peerIDData]];\n            [tmpData appendData:peerIDData];\n        }\n    }\n    return TRUE;\n}\n\n- (BOOL)putIncomingDataInCache:(NSData *__nonnull)data {\n    @try {\n        [self.dataCache offer:data];\n    }\n    @catch (NSException *e) {\n        [self.logger e:@\"putIncomingDataInCache error: device=%@: cannot add data in cache\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n        return FALSE;\n    }\n    return TRUE;\n}\n\n- (BOOL)handleIncomingData:(NSData *__nonnull)data {\n    [self.logger d:@\"handleIncomingData called: identifier=%@ len=%lu base64=%@\", [self.logger SensitiveNSObject:[self getIdentifier]], [data length], [self.logger SensitiveNSObject:[data base64EncodedStringWithOptions:0]]];\n    if ([self.logger showSensitiveData]) {\n        [BleManager printLongLog:[BleManager NSDataToHex:data]];\n    }\n\n    if (self.l2capClientHandshakeRunning) {\n        [self.l2capHandshakeRecvData appendBytes:data length:[data length]];\n        if ([self.l2capHandshakeRecvData length] < L2CAP_HANDSHAKE_DATA) {\n            [self.logger d:@\"handleIncomingData: device=%@: client handshake received incompleted payload: length=%lu\", [self.logger SensitiveNSObject:[self getIdentifier]], [self.l2capHandshakeRecvData length]];\n        } else if ([self.l2capHandshakeRecvData length] == L2CAP_HANDSHAKE_DATA) {\n            if ([data isEqualToData:self.l2capHandshakeData]) {\n                [self.logger d:@\"handleIncomingData: device=%@: client handshake received payload\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n                self.l2capHandshakeStepStatus = TRUE;\n                dispatch_block_cancel(self.l2capHandshakeBlock);\n                [self.l2capHandshakeLatch countDown];\n            } else {\n                [self.logger e:@\"handleIncomingData: device=%@: client handshake received wrong payload\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n                dispatch_block_cancel(self.l2capHandshakeBlock);\n                [self.l2capHandshakeLatch countDown];\n                [self.manager disconnect:self];\n            }\n        } else {\n            [self.logger e:@\"handleIncomingData: device=%@: client handshake received bigger payload than expected: length=%lu\", [self.logger SensitiveNSObject:[self getIdentifier]], [self.l2capHandshakeRecvData length]];;\n        }\n    } else if (self.l2capServerHandshakeRunning) {\n        if (!self.l2capHandshakeStepStatus) {\n            [self.logger d:@\"handleIncomingData: device=%@: server handshake received payload, going to write it back\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n\n            // the server side needs to know when it receives all 1st step data, so it must count data len\n            self.l2capHandshakeRecvDataLen += [data length];\n            if (self.l2capHandshakeRecvDataLen == L2CAP_HANDSHAKE_DATA) {\n                self.l2capHandshakeStepStatus = TRUE;\n                self.l2capHandshakeRecvDataLen = 0;\n            }\n\n            if (![self l2capWrite:data]) {\n                [self.logger e:@\"handleIncomingData: device=%@: server handshake write error\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n                self.l2capServerHandshakeRunning = FALSE;\n                self.l2capHandshakeStepStatus = FALSE;\n            }\n        } else if ([data isEqualToData:[self.manager.localPID dataUsingEncoding:NSUTF8StringEncoding]]) {\n            [self.logger d:@\"handleIncomingData: device=%@: server handshake received second payload\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n            self.l2capServerHandshakeRunning = FALSE;\n            self.useL2cap = TRUE;\n        } else {\n            [self.logger e:@\"handleIncomingData: device=%@: server handshake received wrong payload\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n        }\n    } else {\n        if (!self.peer) {\n            [self.logger e:@\"handleIncomingData: device=%@: peer not existing\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n            return [self putIncomingDataInCache:data];\n        }\n\n        if (![self.peer isConnected]) {\n            [self.logger d:@\"handleIncomingData: device=%@: peer not connected, put data in cache\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n            return [self putIncomingDataInCache:data];\n        }\n\n        [self.readQ add:^{\n            BLEBridgeReceiveFromPeer(self.remotePeerID, data);\n            [self.readQ completedTask:nil];\n        } withCallback:nil withDelay:0];\n    }\n    return TRUE;\n}\n\n// Need to copy blocks into the heap because writing is async and the handshake function's stack should not be available\n- (void)handshake {\n    if (![self writeToCharacteristic:[self.manager.localPID dataUsingEncoding:NSUTF8StringEncoding] forCharacteristic:self.peerIDCharacteristic withEOD:TRUE]) {\n        [self.manager disconnect:self];\n        return ;\n    }\n\n    if (![self readToCharacteristic:self.peerIDCharacteristic]) {\n        [self.manager disconnect:self];\n        return ;\n    }\n\n    [self negotiateL2cap];\n\n    if (![self setNotifyValue]) {\n        [self.manager disconnect:self];\n    }\n}\n\n- (BOOL)setNotifyValue {\n    if (self.peripheral != nil && self.peripheral.state == CBPeripheralStateConnected) {\n        [self.logger d:@\"setNotifyValue: going to subscribe to writer notifications\"];\n        [self.writeQ add:^{\n            if (self.peripheral != nil && self.peripheral.state == CBPeripheralStateConnected) {\n                [self.logger d:@\"setNotifyValue: subscribing to writer notifications\"];\n                [self.peripheral setNotifyValue:TRUE forCharacteristic:self.writerCharacteristic];\n            }\n        } withCallback:nil withDelay:0];\n\n        return TRUE;\n    }\n\n    return FALSE;\n}\n\n- (void)peripheral:(CBPeripheral *)peripheral didModifyServices:(NSArray<CBService *> *)invalidatedServices {\n    CBService *service = getService(invalidatedServices, [BleManager.serviceUUID UUIDString]);\n    if (service == nil) {\n        return;\n    }\n    [self.logger d:@\"didModifyServices: device=%@ service=%@\", [self.logger SensitiveNSObject:[self getIdentifier]], invalidatedServices];\n\n    [self.manager disconnect:self];\n}\n\n- (void)handleConnect:(NSError *)error {\n    [self.connectionQ completedTask:error];\n\n    if (error) {\n        [self.logger e:@\"handleConnect error: device=%@ error=%@\", [self.logger SensitiveNSObject:[self getIdentifier]], error];\n        [self.manager disconnect:self];\n        return;\n    }\n\n    [self.logger i:@\"handleConnect: device=%@: connection successed\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n    [self discoverServices:@[self.manager.serviceUUID]];\n}\n\n- (void)connectWithOptions:(NSDictionary *)options {\n    [self.logger d:@\"connectWithOptions called: device=%@\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n    [self.connectionQ add:^{\n        [self.logger d:@\"connectWithOptions: device=%@: in queue for connecting\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n        [self.manager.cManager connectPeripheral:self.peripheral options:nil];\n    } withCallback:nil withDelay:0];\n}\n\n#pragma mark - write functions\n\n- (void)flushCache {\n    [self.logger d:@\"flushCache called: device=%@\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n\n    while ([self.dataCache element] != [NSNull null]) {\n        NSData *data = [[self.dataCache poll] retain];\n        [self.logger d:@\"flushCache: device=%@ base64=%@ data=%@\", [self.logger SensitiveNSObject:[self getIdentifier]], [self.logger SensitiveNSObject:[data base64EncodedStringWithOptions:0]], [self.logger SensitiveNSObject:[BleManager NSDataToHex:data]]];\n        BLEBridgeReceiveFromPeer(self.remotePeerID, data);\n        [data release];\n    }\n}\n\n- (NSData *)getDataToSend {\n    NSData *result = nil;\n\n    if (self.remainingData == nil || self.remainingData.length <= 0) {\n        return result;\n    }\n\n    NSUInteger chunckSize = self.remainingData.length > [self.peripheral maximumWriteValueLengthForType:CBCharacteristicWriteWithResponse] ? [self.peripheral maximumWriteValueLengthForType:CBCharacteristicWriteWithResponse] : self.remainingData.length;\n\n    result = [NSData dataWithBytes:[self.remainingData bytes] length:chunckSize];\n\n    if (self.remainingData.length <= chunckSize) {\n        self.remainingData = nil;\n    } else {\n        self.remainingData = [[NSData alloc]\n                              initWithBytes:[self.remainingData bytes] + chunckSize\n                              length:[self.remainingData length] - chunckSize];\n    }\n\n    return result;\n}\n\n- (BOOL)writeToCharacteristic:(NSData *)data forCharacteristic:(CBCharacteristic *)characteristic withEOD:(BOOL)eod {\n    @synchronized (self.writerLatch) {\n        [self.logger d:@\"writeToCharacteristic called: device=%@ base64=%@\", [self.logger SensitiveNSObject:[self getIdentifier]], [self.logger SensitiveNSObject:[data base64EncodedStringWithOptions:0]]];\n        if ([self.logger showSensitiveData]) {\n            [BleManager printLongLog:[BleManager NSDataToHex:data]];\n        }\n\n        __block BOOL success = FALSE;\n        NSData *toSend = nil;\n        self.remainingData = data;\n\n        while (self.remainingData.length > 0) {\n            if (self.peripheral != nil && self.peripheral.state == CBPeripheralStateConnected) {\n                toSend = [[self getDataToSend] retain];\n                CountDownLatch *countDownLatch = [[CountDownLatch alloc] initCount:1];\n\n                [self.logger d:@\"writeToCharacteristic: device=%@: going to write payload=%@\", [self.logger SensitiveNSObject:[self getIdentifier]], [self.logger SensitiveNSObject:[toSend base64EncodedStringWithOptions:0]]];\n                [self.writeQ add:^{\n                    if (self.peripheral == nil || self.peripheral.state != CBPeripheralStateConnected) {\n                        [self.logger e:@\"writeToCharacteristic error: device=%@ not connected\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n                        success = FALSE;\n                        [countDownLatch countDown];\n                        return ;\n                    }\n\n                    [self.logger d:@\"writeToCharacteristic: device=%@: writing base64=%@ data=%@\", [self.logger SensitiveNSObject:[self getIdentifier]], [self.logger SensitiveNSObject:[toSend base64EncodedStringWithOptions:0]], [self.logger SensitiveNSObject:[BleManager NSDataToHex:toSend]]];\n                    [self.peripheral writeValue:toSend forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];\n                } withCallback:^(NSError *error){\n                    [self.logger d:@\"writeToCharacteristic: device=%@: callback called for payload=%@ status=%@\", [self.logger SensitiveNSObject:[self getIdentifier]], [self.logger SensitiveNSObject:[toSend base64EncodedStringWithOptions:0]], error];\n                    success = error == nil ? TRUE : FALSE;\n                    [countDownLatch countDown];\n                } withDelay:0];\n\n                [countDownLatch await];\n                [countDownLatch release];\n\n                [toSend release];\n\n                // don't write EOD is error occurred\n                if (!success) {\n                    [self.logger e:@\"writeToCharacteristic error: device=%@: cancellation of the following writes\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n                    return FALSE;\n                }\n            } else {\n                [self.logger e:@\"writeToCharacteristic error: device=%@ not connected\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n                return FALSE;\n            }\n        }\n\n        if (eod) {\n            dispatch_semaphore_t sema = dispatch_semaphore_create(0);\n\n            [self.logger d:@\"writeToCharacteristic: device=%@ going to write EOD\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n            [self.writeQ add:^{\n                [self.logger d:@\"writeToCharacteristic: device=%@ writing EOD\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n                [self.peripheral writeValue:[@\"EOD\" dataUsingEncoding:NSUTF8StringEncoding] forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];\n            } withCallback:^(NSError *error){\n                [self.logger d:@\"writeToCharacteristic: device=%@: callback called for EOD\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n                success = error == nil ? 1 : 0;\n                dispatch_semaphore_signal(sema);\n            } withDelay:0];\n\n            dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);\n            dispatch_release(sema);\n        }\n\n        return success;\n    }\n}\n\n- (BOOL)readToCharacteristic:(CBCharacteristic *) characteristic {\n    [self.logger d:@\"readToCharacteristic called: device=%@\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n\n    if (self.peripheral == nil || self.peripheral.state != CBPeripheralStateConnected) {\n        [self.logger e:@\"readToCharacteristic error: device=%@ is not connected\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n        return FALSE;\n    }\n\n    __block BOOL success = FALSE;\n    CountDownLatch *countDownLatch = [[CountDownLatch alloc] initCount:1];\n\n    [self.writeQ add:^{\n        [self.logger d:@\"readToCharacteristic: device=%@: in queue\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n\n        if (self.peripheral == nil || self.peripheral.state != CBPeripheralStateConnected) {\n            [self.logger e:@\"readToCharacteristic: device=%@ is not connected\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n            success = FALSE;\n            [countDownLatch countDown];\n            return ;\n        }\n\n        [self.peripheral readValueForCharacteristic:characteristic];\n    } withCallback:^(NSError *error){\n        if (error == nil) {\n            [self.logger d:@\"readToCharacteristic: device=%@: callback called with success\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n            success = TRUE;\n        } else {\n            [self.logger e:@\"readToCharacteristic error: device=%@ error=%@ in callback\", [self.logger SensitiveNSObject:[self getIdentifier]], error];\n            success = FALSE;\n        }\n        [countDownLatch countDown];\n    } withDelay:0];\n\n    [countDownLatch await];\n    [countDownLatch release];\n\n    return success;\n}\n\n- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {\n    [self.writeQ completedTask:error];\n\n    if (error) {\n        [self.logger e:@\"didUpdateNotificationStateForCharacteristic error: device=%@ characteristic=%@ error=%@\", [self.logger SensitiveNSObject:[self getIdentifier]], [characteristic.UUID UUIDString], error];\n        [self.manager disconnect:self];\n        return;\n    }\n\n    self.peer = [self.manager.peerManager registerDevice:self withPeerID:self.remotePeerID isClient:TRUE];\n    if (self.peer == nil) {\n        [self.logger e:@\"didUpdateNotificationStateForCharacteristic error: device=%@: registerDevice failed\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n        [self.manager disconnect:self];\n    } else {\n        [self.logger d:@\"didUpdateNotificationStateForCharacteristic: device=%@: registerDevice successed\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n    }\n}\n\n// Called when the value of the characteristic changed, whether by readValueForCharacteristic: or by a notification after a subscription\n- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error {\n    [self.logger d:@\"didUpdateValueForCharacteristic called: device=%@ characteristic=%@\", [self.logger SensitiveNSObject:[self getIdentifier]], [characteristic.UUID UUIDString]];\n\n    if (error) {\n        [self.logger e:@\"didUpdateValueForCharacteristic error: device=%@ error=%@\", [self.logger SensitiveNSObject:[self getIdentifier]], error];\n        [self.manager disconnect:self];\n        [self.writeQ completedTask:error];\n        return;\n    }\n\n    if ([characteristic.UUID isEqual:self.manager.peerUUID]) {\n        if (characteristic.value != nil) {\n            int psm;\n            [[characteristic.value subdataWithRange:NSMakeRange(0, 4)] getBytes:&psm length:sizeof(psm)];\n            self.psm = NSSwapBigIntToHost(psm);\n            NSString* remotePeerID = [NSString stringWithUTF8String: [[characteristic.value subdataWithRange:NSMakeRange(4, characteristic.value.length - 4)] bytes]];\n\n            [self.logger d:@\"didUpdateValueForCharacteristic: device=%@ PSM=%d remotePID=%@\", [self.logger SensitiveNSObject:[self getIdentifier]], self.psm, [self.logger SensitiveNSObject:remotePeerID]];\n\n            self.remotePeerID = remotePeerID;\n\n            [self.writeQ completedTask:nil];\n        } else {\n            [self.logger e:@\"didUpdateValueForCharacteristic error: device=%@: characteristic doesn't have any value\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n            [self.writeQ completedTask:[NSError errorWithDomain:@LOCAL_DOMAIN code:200 userInfo:@{@\"Error reason\": @\"Empty value\"}]];\n        }\n    } else if ([characteristic.UUID isEqual:self.manager.writerUUID]) {\n        [self handleIncomingData:characteristic.value];\n    } else {\n        [self.logger e:@\"didUpdateValueForCharacteristic error: device=%@: bad characteristic requested\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n    }\n}\n\n- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {\n    [self.logger d:@\"didWriteValueForCharacteristic called: device=%@\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n\n    if (error) {\n        [self.logger e:@\"didWriteValueForCharacteristic error: device=%@ characteristic=%@ error=%@\", [self.logger SensitiveNSObject:[self getIdentifier]], [characteristic.UUID UUIDString], error];\n    }\n\n    [self.writeQ completedTask:error];\n}\n\n#pragma mark - Characteristic Discovery\n\n- (void)discoverCharacteristics:(nullable NSArray *)characteristics forService:(CBService *)service {\n    if (self.peripheral == nil || self.peripheral.state != CBPeripheralStateConnected) {\n        [self.logger e:@\"discoverCharacteristics error: device=%@ is not connected\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n        [self.manager disconnect:self];\n        return ;\n    }\n\n    [self.connectionQ add:^{\n        [self.peripheral discoverCharacteristics:characteristics forService:service];\n    } withCallback:nil withDelay:0];\n}\n\n- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {\n    [self.logger d:@\"didDiscoverCharacteristicsForService called: device=%@\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n\n    [self.connectionQ completedTask:error];\n\n    if (error) {\n        [self.logger e:@\"didDiscoverCharacteristicsForService error: device=%@ error=%@\", [self.logger SensitiveNSObject:[self getIdentifier]], error];\n        [self.manager disconnect:self];\n        return;\n    }\n\n    for (CBCharacteristic *chr in service.characteristics) {\n        if ([chr.UUID isEqual:self.manager.peerUUID]) {\n            self.peerIDCharacteristic = chr;\n            [self.logger d:@\"didDiscoverCharacteristicsForService: device=%@: peerID characteristic found\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n        } else if ([chr.UUID isEqual:self.manager.writerUUID]) {\n            self.writerCharacteristic = chr;\n            [self.logger d:@\"didDiscoverCharacteristicsForService: device=%@: writer characteristic found\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n        }\n    }\n\n    if (self.peerIDCharacteristic == nil || self.writerCharacteristic == nil) {\n        [self.logger e:@\"didDiscoverCharacteristicsForService error: device=%@: not all characteristics found\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n        [self.manager disconnect:self];\n        return ;\n    }\n\n    dispatch_async(dispatch_get_global_queue(0, 0), ^{\n        [self handshake];\n    });\n}\n\n#pragma mark - Services Discovery\n\n- (void)discoverServices:(NSArray *)serviceUUIDs {\n    if (self.peripheral == nil || self.peripheral.state != CBPeripheralStateConnected) {\n        [self.logger e:@\"discoverServices error: device=%@ is not connected\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n        [self.manager disconnect:self];\n        return ;\n    }\n\n    self.peripheral.delegate = self;\n    [self.connectionQ add:^{\n        [self.peripheral discoverServices:serviceUUIDs];\n    } withCallback:nil withDelay:0];\n}\n\n- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {\n    [self.logger d:@\"didDiscoverServices called: device=%@\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n\n    [self.connectionQ completedTask:error];\n\n    if (error) {\n        [self.logger e:@\"didDiscoverServices error: device=%@ error=%@\", [self.logger SensitiveNSObject:[self getIdentifier]], error];\n        [self.manager disconnect:self];\n        return;\n    }\n\n    CBService *service = getService(self.peripheral.services, [self.manager.serviceUUID UUIDString]);\n    if (service == nil) {\n        [self.logger e:@\"didDiscoverServices error: device=%@: service not found\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n        [self.manager disconnect:self];\n        return;\n    }\n    [self discoverCharacteristics:@[self.manager.peerUUID, self.manager.writerUUID,] forService:service];\n}\n\n#pragma mark - L2cap\n\n- (BOOL) negotiateL2cap {\n    [self.logger d:@\"negotiateL2cap called: device=%@\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n\n    if (self.peripheral == nil || self.peripheral.state != CBPeripheralStateConnected) {\n        [self.logger e:@\"negotiateL2cap error: device=%@ is not connected\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n        return FALSE;\n    }\n\n    __block BOOL success = FALSE;\n    CountDownLatch *countDownLatch = [[CountDownLatch alloc] initCount:1];\n\n    if (@available(iOS 11.0, *)) {\n        if (self.psm != 0) {\n            [self.connectionQ add:^{\n                [self.logger d:@\"negotiateL2cap: device=%@: opening L2cap channel\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n                [self.peripheral openL2CAPChannel:self.psm];\n            } withCallback:^(NSError *error){\n                if (error == nil) {\n                    [self.logger d:@\"negotiateL2cap: device=%@: callback called with success\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n                    success = TRUE;\n                } else {\n                    [self.logger e:@\"negotiateL2cap error: device=%@ error=%@ in callback\", [self.logger SensitiveNSObject:[self getIdentifier]], error];\n                    success = FALSE;\n                }\n                [countDownLatch countDown];\n            } withDelay:0];\n\n            [countDownLatch await];\n            [countDownLatch release];\n        } else {\n            [self.logger d:@\"negotiateL2cap: device=%@: central peripheral doesn't support L2CAP, aborting negotiation\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n            success = TRUE; // return TRUE to continue connection without L2cap\n        }\n    } else {\n        [self.logger d:@\"negotiateL2cap: device=%@: iOS 11+ is required\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n        success = TRUE; // return TRUE to continue connection without L2cap\n    }\n\n    return success;\n}\n\n- (BOOL)l2capWrite:(NSData *__nonnull)data {\n    __block BOOL success = FALSE;\n\n    if (self.l2capChannel != nil) {\n        dispatch_semaphore_t sema = dispatch_semaphore_create(0);\n\n        [self.writeQ add:^{\n            @synchronized (self.writerLatch) {\n                [self.logger d:@\"l2capWrite: device=%@ len=%lu base64=%@\", [self.logger SensitiveNSObject:[self getIdentifier]], [data length], [self.logger SensitiveNSObject:[data base64EncodedStringWithOptions:0]]];\n                if ([self.logger showSensitiveData]) {\n                    [BleManager printLongLog:[BleManager NSDataToHex:data]];\n                }\n\n                self.l2capWriteIndex = 0;\n                self.l2capWriteData = data;\n                if ([self.l2capChannel.outputStream hasSpaceAvailable]) {\n                    uint8_t *readBytes = (uint8_t *)[self.l2capWriteData bytes];\n                    NSUInteger data_len = [data length];\n                    NSUInteger len = (data_len >= L2CAP_BUFFER) ? L2CAP_BUFFER : (data_len);\n                    uint8_t buf[len];\n\n                    (void)memcpy(buf, readBytes, len);\n\n                    self.l2capWriteIndex = [self.l2capChannel.outputStream write:(const uint8_t *)buf maxLength:len];\n                    [self.logger d:@\"l2capWrite: device=%@: wrote len=%zd\", [self.logger SensitiveNSObject:[self getIdentifier]], self.l2capWriteIndex];\n\n                    if (self.l2capWriteIndex == -1) {\n                        [self.logger e:@\"l2capWrite error: device=%@\", [self.logger SensitiveNSObject:[self getIdentifier]], self.l2capWriteIndex];\n\n                        self.l2capWriteData = nil;\n                        [self.writeQ completedTask:[NSError errorWithDomain:@LOCAL_DOMAIN code:200 userInfo:@{@\"Error reason\": @\"write error\"}]];\n                        return ;\n                    }\n\n                    if (self.l2capWriteIndex < data_len) { // write next data chunk when callback stream handleEvent: NSStreamEventHasSpaceAvailable is called\n                        [self.logger d:@\"l2capWrite: device=%@: write completed but need more write space to send all data, waiting...\", [self.logger SensitiveNSObject:[self getIdentifier]], self.l2capWriteIndex];\n                    } else {\n                        [self.logger d:@\"l2capWrite: device=%@: write completed and all data send\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n\n                        self.l2capWriteData = nil;\n                        [self.writeQ completedTask:nil];\n                    }\n                } else {\n                    [self.logger d:@\"l2capWrite: device=%@: need some space available, waiting...\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n                }\n            }\n        } withCallback:^(NSError *error) {\n            if (error == nil) {\n                [self.logger d:@\"l2capWrite: device=%@: callback called with success\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n                success = TRUE;\n            } else {\n                [self.logger e:@\"l2capWrite error: device=%@ error=%@ in callback\", [self.logger SensitiveNSObject:[self getIdentifier]], error];\n                success = FALSE;\n            }\n            dispatch_semaphore_signal(sema);\n        } withDelay:0];\n\n        dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);\n        dispatch_release(sema);\n\n        return success;\n    } else {\n        [self.logger e:@\"l2capWrite error: device=%@: channel not set\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n        return FALSE;\n    }\n}\n\n- (void)peripheral:(CBPeripheral *)peripheral didOpenL2CAPChannel:(CBL2CAPChannel *)channel error:(NSError *)error API_AVAILABLE(ios(11.0)) {\n    [self.logger d:@\"didOpenL2CAPChannel called: device=%@\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n    if (error != nil) {\n        [self.logger e:@\"didOpenL2CAPChannel Error: device=%@ error=%@\", [self.logger SensitiveNSObject:[self getIdentifier]], error];\n        [self.connectionQ completedTask:error];\n        return ;\n    }\n\n    self.l2capChannel = channel;\n\n    self.l2capThread = [[NSThread alloc] initWithTarget:self selector:@selector(setupL2capStreams) object:nil];\n    [self.l2capThread start];\n\n    self.l2capClientHandshakeRunning = TRUE;\n    self.useL2cap = [self testL2cap];\n    self.l2capClientHandshakeRunning = FALSE;\n\n    // wait that server complete L2CAP tests\n    [NSThread sleepForTimeInterval:2.0f];\n\n    [self.connectionQ completedTask:nil];\n}\n\n- (void)setupL2capStreams {\n    [self.logger d:@\"setupL2capStreams called: device=%@\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n\n    self.l2capChannel.inputStream.delegate = self;\n    [self.l2capChannel.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];\n    [self.l2capChannel.inputStream open];\n    self.l2capChannel.outputStream.delegate = self;\n    [self.l2capChannel.outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];\n    [self.l2capChannel.outputStream open];\n\n    @autoreleasepool {\n        do {\n            [[NSRunLoop currentRunLoop] run];\n        } while (self.peer != nil && [self.peer isConnected]);\n    }\n}\n\n- (NSMutableData *__nonnull)createRandomNSData:(int) capacity\n{\n    NSMutableData* theData = [NSMutableData dataWithCapacity:capacity];\n\n    for (unsigned int i = 0 ; i < capacity / 4 ; ++i ) {\n        u_int32_t randomBits = arc4random();\n        [theData appendBytes:(void *)&randomBits length:4];\n    }\n    return theData;\n}\n\n// Test contains 2 steps:\n// 1) client sends local PID and waits for receiving remote PID\n// 2) client sends remote PID in response of 1) to the server\n- (BOOL)testL2cap {\n    self.l2capHandshakeStepStatus = FALSE;\n    self.l2capHandshakeRecvData = [NSMutableData dataWithCapacity:L2CAP_HANDSHAKE_DATA];\n    self.l2capHandshakeLatch = [[CountDownLatch alloc] initCount:1];\n\n    self.l2capHandshakeBlock = dispatch_block_create(DISPATCH_BLOCK_INHERIT_QOS_CLASS, ^{\n        [self.logger e:@\"testL2cap: device=%@: timeout hired\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n        [self.l2capHandshakeLatch countDown];\n    });\n    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC), dispatch_get_main_queue(), self.l2capHandshakeBlock);\n\n    // step 1\n    [self.logger d:@\"testL2cap: device=%@: client going to write the 1st payload\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n    self.l2capHandshakeData = [self createRandomNSData:L2CAP_HANDSHAKE_DATA];\n    if (![self l2capWrite:self.l2capHandshakeData]) {\n        [self.logger e:@\"testL2cap error: device=%@: client write error\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n        dispatch_block_cancel(self.l2capHandshakeBlock);\n        self.l2capHandshakeData = nil;\n        self.l2capHandshakeRecvData = nil;\n        return FALSE;\n    }\n\n    // waiting for receiving remote PID\n    [self.l2capHandshakeLatch await];\n    self.l2capHandshakeData = nil;\n    self.l2capHandshakeRecvData = nil;\n\n    // step 2\n    if (self.l2capHandshakeStepStatus) {\n        [self.logger d:@\"testL2cap: device=%@: client going to write the 2nd payload\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n        if (![self l2capWrite:[self.remotePeerID dataUsingEncoding:NSUTF8StringEncoding]]) {\n            [self.logger e:@\"testL2cap error: device=%@: client write error\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n            return FALSE;\n        }\n\n        [self.logger d:@\"testL2cap: device=%@: client handshake completed\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n        return TRUE;\n    }\n\n    return FALSE;\n}\n\n- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {\n    switch(eventCode) {\n        case NSStreamEventNone: {\n            [self.logger d:@\"stream handleEvent: NSStreamEventNone: device=%@\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n            break;\n        }\n        case NSStreamEventOpenCompleted: {\n            [self.logger d:@\"stream handleEvent: NSStreamEventOpenCompleted: device=%@\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n            break;\n        }\n        case NSStreamEventHasBytesAvailable: {\n            [self.logger d:@\"stream handleEvent: NSStreamEventHasBytesAvailable: device=%@\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n\n            uint8_t buf[L2CAP_BUFFER];\n\n            NSInteger len = 0;\n\n            len = [(NSInputStream *)stream read:buf maxLength:L2CAP_BUFFER];\n\n            if(len > 0) {\n                NSData *received = [NSData dataWithBytes:buf length:len];\n                [self.logger d:@\"stream handleEvent: NSStreamEventHasBytesAvailable: device=%@ read length=%lu value=%@\", [self.logger SensitiveNSObject:[self getIdentifier]], len, [self.logger SensitiveNSObject:received]];\n                [self handleIncomingData:received];\n            } else {\n                [self.logger e:@\"stream handleEvent error: NSStreamEventHasBytesAvailable: device=%@: nothing to read\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n            }\n\n            break;\n        }\n        case NSStreamEventHasSpaceAvailable: {\n            [self.logger d:@\"stream handleEvent: NSStreamEventHasSpaceAvailable: device=%@\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n\n            if ((self.peer != nil && [self.peer isConnected]) || self.l2capServerHandshakeRunning || self.l2capClientHandshakeRunning) {\n                @synchronized (self.writerLatch) {\n                    if (self.l2capWriteData != nil) {\n                        uint8_t *readBytes = (uint8_t *)[self.l2capWriteData bytes];\n                        readBytes += self.l2capWriteIndex;\n                        NSUInteger data_len = [self.l2capWriteData length];\n                        NSUInteger len = ((data_len - self.l2capWriteIndex >= L2CAP_BUFFER) ? L2CAP_BUFFER : (data_len - self.l2capWriteIndex));\n                        uint8_t buf[len];\n\n                        (void)memcpy(buf, readBytes, len);\n\n                        if ([self.logger showSensitiveData]) {\n                            [self.logger d:@\"stream handleEvent: NSStreamEventHasSpaceAvailable: device=%@ offset=%lu len=%lu base64=%@ data=%@\", [self getIdentifier], self.l2capWriteIndex, len, [[NSData dataWithBytes:buf length:len] base64EncodedStringWithOptions:0], [BleManager NSDataToHex:[NSData dataWithBytes:buf length:len]]];\n                        }\n                        NSInteger wroteLen = [(NSOutputStream *)stream write:(const uint8_t *)buf maxLength:len];\n                        [self.logger d:@\"stream handleEvent: NSStreamEventHasSpaceAvailable: device=%@ wrote data offset=%lu len=%zd\", [self.logger SensitiveNSObject:[self getIdentifier]], self.l2capWriteIndex, wroteLen];\n\n                        if (wroteLen == -1) {\n                            [self.logger e:@\"stream handleEvent error: NSStreamEventHasSpaceAvailable: device=%@ write: error\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n                            self.l2capWriteData = nil;\n                            [self.writeQ completedTask:[NSError errorWithDomain:@LOCAL_DOMAIN code:200 userInfo:@{@\"Error reason\": @\"write error\"}]];\n\n                            break;\n                        }\n\n                        self.l2capWriteIndex += wroteLen;\n                        if ([self.l2capWriteData length] == self.l2capWriteIndex) {\n                            [self.logger d:@\"stream handleEvent: NSStreamEventHasSpaceAvailable: device=%@: write completed\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n\n                            self.l2capWriteData = nil;\n                            [self.writeQ completedTask:nil];\n                        }\n                    } else {\n                        [self.logger d:@\"stream handleEvent: NSStreamEventHasSpaceAvailable: device=%@: no data to write\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n                    }\n                }\n            } else {\n                [self.logger e:@\"stream handleEvent error: NSStreamEventHasSpaceAvailable: device=%@: device is not connected\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n            }\n\n            break;\n        }\n        case NSStreamEventErrorOccurred: {\n            [self.logger d:@\"stream handleEvent: NSStreamEventErrorOccurred: device=%@\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n\n            [self.manager disconnect:self];\n            break;\n        }\n        case NSStreamEventEndEncountered: { // (d4ryl00): not sure how to handle this case\n            [self.logger d:@\"stream handleEvent: NSStreamEventEndEncountered: device=%@\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n\n            if (self.l2capChannel.outputStream == stream) {\n                NSData *newData = [stream propertyForKey:NSStreamDataWrittenToMemoryStreamKey];\n\n                if (!newData) {\n                    [self.logger d:@\"stream handleEvent: NSStreamEventEndEncountered: device=%@: no more data\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n                } else {\n                    [self.logger d:@\"stream handleEvent: NSStreamEventEndEncountered: device=%@: data to process\", [self.logger SensitiveNSObject:[self getIdentifier]]];\n                    [self handleIncomingData:newData];\n                }\n            }\n\n            stream.delegate = nil;\n            [stream close];\n            [stream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];\n            if (self.l2capThread != nil) {\n                [self.l2capThread cancel];\n                [self.l2capThread release];\n                self.l2capThread = nil;\n            }\n\n            [self.manager disconnect:self];\n\n            break;\n        }\n    }\n}\n\n@end\n"
  },
  {
    "path": "pkg/ble-driver/BleInterface_darwin.h",
    "content": "//\n//  BleInterface.h\n//  ble\n//\n//  Created by sacha on 26/09/2018.\n//  Copyright © 2018 sacha. All rights reserved.\n//\n\n#import <Foundation/Foundation.h>\n#import <CoreBluetooth/CoreBluetooth.h>\n#import <os/log.h>\n#import <signal.h>\n\n#import \"BleManager_darwin.h\"\n#import \"Logger.h\"\n\n#ifndef BleInterface_h\n#define BleInterface_h\n\nvoid BLEStart(char *localPID);\nvoid BLEStop(void);\nint BLESendToPeer(char *remotePID, void *payload, int length);\nint BLEDialPeer(char *remotePID);\nvoid BLECloseConnWithPeer(char *remotePID);\nint BLEBridgeHandleFoundPeer(NSString *remotePID);\nvoid BLEBridgeHandleLostPeer(NSString *remotePID);\nvoid BLEBridgeReceiveFromPeer(NSString *remotePID, NSData *payload);\nvoid BLEBridgeLog(enum level level, NSString *message);\nvoid BLEUseExternalLogger(void);\n\n#endif /* BleInterface_h */\n"
  },
  {
    "path": "pkg/ble-driver/BleInterface_darwin.m",
    "content": "// +build darwin,!noproximitytransport\n//\n//  BleInterface.m\n//  ble\n//\n//  Created by sacha on 26/09/2018.\n//  Copyright © 2018 sacha. All rights reserved.\n//\n\n#import \"BleInterface_darwin.h\"\n\n// This functions are Go functions so they aren't defined here\nextern int BLEHandleFoundPeer(char *);\nextern void BLEHandleLostPeer(char *);\nextern void BLEReceiveFromPeer(char *, void *, unsigned long);\nextern void BLELog(enum level level, const char *message);\n\nstatic BleManager *manager = nil;\nBOOL gBLEUseExternalLogger = FALSE;\n\nvoid handleException(NSException* exception) {\n    NSLog(@\"Unhandled exception %@\", exception);\n}\n\nBleManager* getManager(void) {\n    @synchronized([BleManager class])\n    {\n        if(!manager) {\n            NSLog(@\"BleManager: initialization\");\n            manager = [[BleManager alloc] initDriver:gBLEUseExternalLogger];\n        }\n    }\n    return manager;\n}\n\nvoid releaseManager(void) {\n    @synchronized([BleManager class])\n    {\n        if(manager) {\n            NSLog(@\"releaseManager\");\n            [manager release];\n            manager = nil;\n        }\n    }\n}\n\n#pragma mark - incoming API functions\n\nvoid BLEStart(char *localPID) {\n    NSLog(@\"BLEStart called\");\n    @autoreleasepool {\n        NSString *localPIDString = [NSString stringWithUTF8String:localPID];\n        [getManager() setLocalPID:localPIDString];\n        [getManager().logger i:@\"BLEStart: pid=%@\", [getManager().logger SensitiveNSObject:localPIDString]];\n        [getManager() setID:[localPIDString substringWithRange:NSMakeRange([localPIDString length] - 4, 4)]];\n        [getManager() startScanning];\n        [getManager() startAdvertising];\n        NSSetUncaughtExceptionHandler(handleException);\n    }\n}\n\n// TODO: Implement this, check if error\nvoid BLEStop(void) {\n    [getManager().logger i:@\"BLEStop\"];\n    [getManager() stopScanning];\n    [getManager() stopAdvertising];\n    [getManager() closeAllConnections];\n    releaseManager();\n}\n\nint BLESendToPeer(char *remotePID, void *payload, int length) {\n    int status = 0;\n\n    NSString *cPID = [[NSString alloc] initWithUTF8String:remotePID];\n    NSData *cPayload = [[NSData alloc] initWithBytes:payload length:length];\n\n    BertyDevice *bDevice = [getManager() findPeripheralFromPID:cPID];\n    if (bDevice == nil) {\n        [getManager().logger e:@\"BLESendToPeer error: no device found\"];\n        return 0;\n    }\n\n    if (bDevice.peer == nil) {\n        [getManager().logger e:@\"BLESendToPeer error: peer object not found\"];\n        return 0;\n    }\n\n    if (bDevice.useL2cap && bDevice.l2capChannel != nil) {\n        status = [bDevice l2capWrite:cPayload];\n    } else {\n        if ([bDevice.peer isClientReady]) {\n            status = [bDevice writeToCharacteristic:cPayload forCharacteristic:bDevice.writerCharacteristic withEOD:FALSE];\n        } else if ([bDevice.peer isServerReady]) {\n            status = [getManager() writeAndNotify:bDevice data:cPayload];\n        } else {\n            [getManager().logger e:@\"BLESendToPeer error: device not connected\"];\n        }\n    }\n\n    [cPID release];\n    [cPayload release];\n    return status;\n}\n\nint BLEDialPeer(char *remotePID) {\n    BertyDevice *bDevice = [getManager() findPeripheralFromPID:[NSString stringWithUTF8String:remotePID]];\n    if (bDevice != nil) {\n        return 1;\n    }\n    return 0;\n}\n\n// TODO: Implement this\nvoid BLECloseConnWithPeer(char *remotePID) {\n    [getManager().logger i:@\"BLECloseConnWithPeer called: remotePID=%@\", [getManager().logger SensitiveString:remotePID]];\n    BertyDevice *bDevice = [getManager() findPeripheralFromPID:[NSString stringWithUTF8String:remotePID]];\n    if (bDevice != nil) {\n        [getManager() disconnect:bDevice];\n    }\n}\n\n\n// Use BLEBridgeLog to write logs to the external logger\nvoid BLEUseExternalLogger(void) {\n    gBLEUseExternalLogger = TRUE;\n}\n\n#pragma mark - outgoing API functions\n\nint BLEBridgeHandleFoundPeer(NSString *remotePID) {\n    char *cPID = (char *)[remotePID UTF8String];\n    if (BLEHandleFoundPeer(cPID)) {\n        return (1);\n    }\n    return (0);\n}\n\nvoid BLEBridgeHandleLostPeer(NSString *remotePID) {\n    char *cPID = (char *)[remotePID UTF8String];\n    BLEHandleLostPeer(cPID);\n}\n\nvoid BLEBridgeReceiveFromPeer(NSString *remotePID, NSData *payload) {\n    char *cPID = (char *)[remotePID UTF8String];\n    char *cPayload = (char *)[payload bytes];\n    int length = (int)[payload length];\n    BLEReceiveFromPeer(cPID, cPayload, length);\n}\n\n// Write logs to the external logger\nvoid BLEBridgeLog(enum level level, NSString *message) {\n    char *cMessage = (char *)[message UTF8String];\n    BLELog(level, cMessage);\n}\n"
  },
  {
    "path": "pkg/ble-driver/BleManager_darwin.h",
    "content": "//\n//  BleManager.h\n//  ble\n//\n//  Created by sacha on 23/05/2019.\n//  Copyright © 2019 berty. All rights reserved.\n//\n\n#import <Foundation/Foundation.h>\n#import <CoreBluetooth/CoreBluetooth.h>\n\n#import \"BleInterface_darwin.h\"\n#import \"BertyDevice_darwin.h\"\n#import \"PeerManager.h\"\n#import \"CountDownLatch_darwin.h\"\n#import \"WriteDataCache.h\"\n\n#define LOCAL_DOMAIN \"tech.berty.bty\"\n\nNS_ASSUME_NONNULL_BEGIN\n\n@interface BleManager : NSObject <CBPeripheralManagerDelegate, CBCentralManagerDelegate>\n\n+ (CBUUID *__nonnull)serviceUUID;\n+ (CBUUID *__nonnull)peerUUID;\n+ (CBUUID *__nonnull)writerUUID;\n+ (NSString *__nonnull)NSDataToHex:(NSData *__nonnull)data;\n+ (void) printLongLog:(NSString *__nonnull)message;\n\n@property (nonatomic, strong, nullable) Logger *logger;\n@property (nonatomic, strong, nonnull) PeerManager *peerManager;\n@property (readwrite) BOOL pmEnable;\n@property (readwrite) BOOL cmEnable;\n@property (readwrite) int psm;\n@property (nonatomic, strong, nonnull) CBMutableService *bertyService;\n@property (nonatomic, strong, nonnull) CBMutableService *nameService;\n@property (nonatomic, strong, nonnull) CBMutableCharacteristic *peerIDCharacteristic;\n@property (nonatomic, strong, nonnull) CBMutableCharacteristic *writerCharacteristic;\n@property (nonatomic, strong, nullable) NSString *localPID;\n@property (nonatomic, strong, nonnull) NSString *ID;\n@property (nonatomic, strong, nonnull) CBUUID *serviceUUID;\n@property (nonatomic, strong, nonnull) CBUUID *peerUUID;\n@property (nonatomic, strong, nonnull) CBUUID *writerUUID;\n@property (nonatomic, strong, nonnull) NSMutableArray *bDevices;\n@property (nonatomic, strong, nonnull) CBCentralManager* cManager;\n@property (nonatomic, strong, nonnull) CBPeripheralManager* pManager;\n@property (nonatomic, strong, nonnull) CountDownLatch *bleOn;\n@property (nonatomic, strong, nonnull) CountDownLatch *serviceAdded;\n@property (nonatomic, strong, nullable) NSTimer *scannerTimer;\n@property (nonatomic, readwrite, getter=isScanning) BOOL scanning;\n@property (nonatomic, strong, nullable) WriteDataCache *writeCache;\n@property (nonatomic, strong, nullable) CountDownLatch *writerLactch;\n@property (readwrite) BOOL writeStatus;\n\n- (instancetype __nonnull) initDriver:(BOOL)useExternalLogger;\n- (void)addService;\n- (void)startScanning;\n- (void)toggleScanner:(NSTimer *__nonnull)timer;\n- (void)stopScanning;\n- (void)startAdvertising;\n- (void)stopAdvertising;\n- (void)disconnect:(BertyDevice *__nonnull)device;\n- (void)closeAllConnections;\n- (BertyDevice *__nullable)findPeripheralFromIdentifier:(NSUUID *__nonnull)identifier;\n- (BertyDevice *__nullable)findPeripheralFromPID:(NSString *__nonnull)peerID;\n- (BOOL)writeAndNotify:(BertyDevice *__nonnull)device data:(NSData *__nonnull)data;\n\n@end\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "pkg/ble-driver/BleManager_darwin.m",
    "content": "// +build darwin,!noproximitytransport\n//\n//  BleManager.m\n//  ble\n//\n//  Created by sacha on 23/05/2019.\n//  Copyright © 2019 berty. All rights reserved.\n//\n\n#import \"BleManager_darwin.h\"\n\n@implementation BleManager\nstatic NSString* const __nonnull SERVICE_UUID = @\"00004240-0000-1000-8000-00805F9B34FB\";\nstatic NSString* const __nonnull WRITER_UUID = @\"00004242-0000-1000-8000-00805F9B34FB\";\nstatic NSString* const __nonnull PEER_ID_UUID = @\"00004241-0000-1000-8000-00805F9B34FB\";\n\n+ (CBUUID *)serviceUUID {\n    return [CBUUID UUIDWithString:SERVICE_UUID];\n}\n\n+ (CBUUID *)peerUUID {\n    return [CBUUID UUIDWithString:PEER_ID_UUID];\n}\n\n+ (CBUUID *)writerUUID {\n    return [CBUUID UUIDWithString:WRITER_UUID];\n}\n\nstatic inline char itoh(int i) {\n    if (i > 9) return 'A' + (i - 10);\n    return '0' + i;\n}\n\n+ (NSString *__nonnull)NSDataToHex:(NSData *__nonnull)data {\n    NSUInteger i, len;\n    unsigned char *buf, *bytes;\n\n    len = data.length;\n    bytes = (unsigned char*)data.bytes;\n    buf = malloc(len*2);\n\n    for (i=0; i<len; i++) {\n        buf[i*2] = itoh((bytes[i] >> 4) & 0xF);\n        buf[i*2+1] = itoh(bytes[i] & 0xF);\n    }\n\n    return [[[NSString alloc] initWithBytesNoCopy:buf\n                                           length:len*2\n                                         encoding:NSASCIIStringEncoding\n                                     freeWhenDone:YES] autorelease];\n}\n\n+ (void) printLongLog:(NSString *__nonnull)message {\n    if ([message length] > 4000) {\n        NSLog(@\"message.length=%lu\", [message length]);\n        unsigned long int chunkCount = [message length] / 4000;     // integer division\n        for (int i = 0; i <= chunkCount; i++) {\n            int max = 4000 * (i + 1);\n            if (max >= [message length]) {\n                NSLog(@\"chunk %d of %lu: %@\", i, chunkCount, [message substringWithRange:NSMakeRange(4000 * i, [message length] - (4000 * i))]);\n            } else {\n                NSLog(@\"chunk %d of %lu: %@\", i, chunkCount, [message substringWithRange:NSMakeRange(4000 * i, 4000)]);\n            }\n        }\n    } else {\n        NSLog(@\"%@\", message);\n    }\n}\n\n// TODO: No need to check error on this?\n- (instancetype __nonnull) initDriver:(BOOL)useExternalLogger {\n    self = [super init];\n\n    if (self) {\n        BOOL showSensitiveData = FALSE;\n        if (useExternalLogger) {\n            _logger = [[Logger alloc] initWithExternalLoggerAndShowSensitiveData:showSensitiveData];\n        } else {\n            _logger = [[Logger alloc] initLocalLoggerWithSubSystem:LOCAL_DOMAIN andCategorie:\"BLE\" showSensitiveData:showSensitiveData];\n        }\n        _peerManager = [[PeerManager alloc] initWithLogger:_logger];\n        _cmEnable = FALSE;\n        _pmEnable = FALSE;\n        _scannerTimer = nil;\n        _bleOn = [[CountDownLatch alloc] initCount:2];\n        _serviceAdded = [[CountDownLatch alloc] initCount:1];\n        _bDevices = [[NSMutableArray alloc] init];\n\n        _cManager = [[CBCentralManager alloc]\n                     initWithDelegate:self\n                     queue:dispatch_queue_create(\"CentralManager\", DISPATCH_QUEUE_SERIAL)\n                     options:@{CBCentralManagerOptionShowPowerAlertKey:[NSNumber numberWithBool:NO]}];\n\n        _pManager = [[CBPeripheralManager alloc]\n                     initWithDelegate:self\n                     queue:dispatch_queue_create(\"PeripheralManager\", DISPATCH_QUEUE_SERIAL)\n                     options:@{CBPeripheralManagerOptionShowPowerAlertKey:[NSNumber numberWithBool:NO]}];\n\n        [self initService];\n        [self addService];\n    }\n\n    return self;\n}\n\n- (void)initService {\n    [self.logger d:@\"initService called\"];\n\n    _scanning = FALSE;\n    _serviceUUID = [[CBUUID UUIDWithString:SERVICE_UUID] retain];\n    _peerUUID = [[CBUUID UUIDWithString:PEER_ID_UUID] retain];\n    _writerUUID = [[CBUUID UUIDWithString:WRITER_UUID] retain];\n\n    _peerIDCharacteristic = [[CBMutableCharacteristic alloc]\n                             initWithType:self.peerUUID\n                             properties:CBCharacteristicPropertyRead | CBCharacteristicPropertyWrite\n                             value:nil\n                             permissions:CBAttributePermissionsReadable | CBAttributePermissionsWriteable];\n\n    _writerCharacteristic = [[CBMutableCharacteristic alloc]\n                             initWithType:self.writerUUID\n                             properties:CBCharacteristicPropertyWrite | CBCharacteristicPropertyNotify\n                             value:nil\n                             permissions:CBAttributePermissionsWriteable];\n\n    _bertyService = [[CBMutableService alloc] initWithType:self.serviceUUID\n                                                   primary:YES];\n\n    _bertyService.characteristics = [@[self.writerCharacteristic,\n                                       self.peerIDCharacteristic] retain];\n}\n\n- (void)dealloc {\n    [_logger release];\n    [_peerManager release];\n    [_bleOn release];\n    [_serviceAdded release];\n    [_bDevices release];\n    [_cManager release];\n    [_pManager release];\n    [_serviceUUID release];\n    [_peerUUID release];\n    [_writerUUID release];\n    [_peerIDCharacteristic release];\n    [_writerCharacteristic release];\n    [_bertyService.characteristics release];\n    [_bertyService release];\n\n    [super dealloc];\n}\n\n- (void)addService {\n    [self.logger d:@\"addService: service=%@\", [self.serviceUUID UUIDString]];\n\n    [self.bleOn await:5 withCancelBlock:^{\n        [self.logger e:@\"addService error: timeout\"];\n    }];\n    if (self.cmEnable && self.pmEnable) {\n        [self.pManager addService:self.bertyService];\n        [self.serviceAdded await];\n    }\n}\n\n- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(nullable NSError *)error {\n    if (error) {\n        [self.logger e:@\"didAddService() error=%@\", [error localizedFailureReason]];\n    }\n    [self.logger d:@\"didAddService: service=%@\", [service.UUID UUIDString]];\n    [self.serviceAdded countDown];\n}\n\n#pragma mark - go called functions\n\n- (void)startScanning {\n    @synchronized (self.cManager) {\n        if (self.cmEnable && !self.scanning) {\n            if (self.localPID != nil) {\n                [self.logger d:@\"startScanning called\"];\n\n                NSDictionary *options = [NSDictionary\n                                         dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES],\n                                         CBCentralManagerScanOptionAllowDuplicatesKey, nil];\n                [self.cManager scanForPeripheralsWithServices:@[self.serviceUUID] options:options];\n                self.scanning = TRUE;\n\n                dispatch_async(dispatch_get_main_queue(), ^(void){\n                    self.scannerTimer = [NSTimer scheduledTimerWithTimeInterval:12.0 target:self selector:@selector(toggleScanner:) userInfo:nil repeats:YES];\n                });\n            }  else {\n                [self.logger e:@\"startScanning error: localPID is null\"];\n            }\n        } else {\n            [self.logger i:@\"startScanning: scanner is already enabled\"];\n        }\n    }\n}\n\n- (void)toggleScanner:(NSTimer*)timer {\n    if ([self.cManager isScanning]) {\n        [self.logger d:@\"toggleScanner: disable scanner\"];\n        [self.cManager stopScan];\n    } else {\n        [self.logger d:@\"toggleScanner: enable scanner\"];\n        NSDictionary *options = [NSDictionary\n                                 dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES],\n                                 CBCentralManagerScanOptionAllowDuplicatesKey, nil];\n        [self.cManager scanForPeripheralsWithServices:@[self.serviceUUID] options:options];\n    }\n\n}\n\n- (void)stopScanning {\n    @synchronized (self.cManager) {\n        if (self.cmEnable && self.scanning) {\n            [self.logger d:@\"stopScanning called\"];\n\n            dispatch_async(dispatch_get_main_queue(), ^{\n                if (self.scannerTimer != nil) {\n                    if ([self.scannerTimer isValid]) {\n                        [self.scannerTimer invalidate];\n                    }\n                    self.scannerTimer = nil;\n                }\n\n                if ([self.cManager isScanning]) {\n                    [self.cManager stopScan];\n                }\n\n                self.scanning = FALSE;\n            });\n        }\n    }\n}\n\n- (void)startAdvertising {\n    @synchronized (self.pManager) {\n        if (self.pmEnable && ![self.pManager isAdvertising]) {\n            if (self.ID != nil) {\n                [self.logger d:@\"startAdvertising called: ID=%@\", [self.logger SensitiveNSObject:self.ID]];\n\n                // publish l2cap channel\n                self.psm = 0;\n                if (@available(iOS 11.0, *)) {\n                    [self.pManager publishL2CAPChannelWithEncryption:false];\n                }\n\n                [self.pManager startAdvertising:@{ CBAdvertisementDataLocalNameKey:self.ID, CBAdvertisementDataServiceUUIDsKey:@[self.serviceUUID]}];\n            } else {\n                [self.logger e:@\"startAdvertising error: local ID is null\"];\n            }\n        }\n    }\n}\n\n- (void)stopAdvertising {\n    @synchronized (self.pManager) {\n        [self.logger d:@\"stopAdvertising called\"];\n        if (self.pmEnable && [self.pManager isAdvertising]) {\n            if (@available(iOS 11.0, *)) {\n                if (self.psm != 0) {\n                    [self.pManager unpublishL2CAPChannel:self.psm];\n                }\n            }\n            [self.pManager stopAdvertising];\n        } else {\n            [self.logger e:@\"stopAdvertising error: advertising not started\"];\n        }\n    }\n}\n\n// Only the client side can disconnect\n- (void)disconnect:(BertyDevice *__nonnull)device {\n    [self.logger d:@\"closeAllConnections called: debice=%@\", [self.logger SensitiveNSObject:[device clientSideIdentifier]]];\n\n    if (device.peripheral != nil && device.clientSideIdentifier != nil) {\n        [self.logger d:@\"disconnect: client device=%@\", [self.logger SensitiveNSObject:[device clientSideIdentifier]]];\n        if (device.peripheral.state == CBPeripheralStateConnecting || device.peripheral.state == CBPeripheralStateConnected) {\n            [self.cManager cancelPeripheralConnection:device.peripheral];\n        } else {\n            [self.logger d:@\"disconnect: client device=%@ not connected\", [self.logger SensitiveNSObject:[device clientSideIdentifier]]];\n            return ;\n        }\n    } else {\n        [device closeBertyDevice];\n    }\n}\n\n- (void)closeAllConnections {\n    [self.logger i:@\"closeAllConnections called\"];\n\n    if (self.cmEnable) {\n        @synchronized (self.bDevices) {\n            for (BertyDevice *device in self.bDevices) {\n                [self disconnect:device];\n            }\n        }\n    }\n}\n\n- (BOOL)writeAndNotify:(BertyDevice *__nonnull)device data:(NSData *__nonnull)data {\n    [self.logger d:@\"writeAndNotify: device=%@ base64=%@\", [self.logger SensitiveNSObject:[device clientSideIdentifier]], [self.logger SensitiveNSObject:[data base64EncodedStringWithOptions:0]]];\n    if ([self.logger showSensitiveData]) {\n        [BleManager printLongLog:[BleManager NSDataToHex:data]];\n    }\n\n    BOOL success = FALSE;\n    NSUInteger mtu = device.cbCentral.maximumUpdateValueLength;\n    NSUInteger offset = 0;\n    NSUInteger dataLen = [data length];\n\n    while (offset < dataLen) {\n        if (![device.peer isServerReady]) {\n            [self.logger e:@\"writeAndNotify error: device=%@ server not connected\", [self.logger SensitiveNSObject:[device clientSideIdentifier]]];\n            return FALSE;\n        }\n\n        self.writerLactch = [[CountDownLatch alloc] initCount:1];\n        NSUInteger toWriteLen = (dataLen - offset) < mtu ? (dataLen - offset) : mtu;\n        NSData *toWrite = [[data subdataWithRange:NSMakeRange(offset, toWriteLen)] retain];\n\n        if ([self.logger showSensitiveData]) {\n            [self.logger d:@\"writeAndNotify: device=%@ mtu=%lu base64=%@ data=%@\", [device getIdentifier], mtu, [toWrite base64EncodedStringWithOptions:0], [BleManager NSDataToHex:toWrite]];\n        }\n\n        // Need to add data to the cache prior to write it because sometime peripheralManagerIsReadyToUpdateSubscribers is called before data is put to the cache\n        @synchronized (self.writeCache) {\n            self.writeCache = [[WriteDataCache alloc] initWithDevice:device withData:toWrite];\n            [self.logger d:@\"writeAndNotify: device=%@: data put in cache successfully\", [self.logger SensitiveNSObject:[device getIdentifier]]];\n        }\n\n        success = [self.pManager updateValue:toWrite forCharacteristic:self.writerCharacteristic onSubscribedCentrals:@[device.cbCentral]];\n\n        if (success) {\n            [self.writerLactch countDown];\n        } else {\n            [self.logger d:@\"writeAndNotify: device=%@: operation queue is full and will be processed by the peripheralManagerIsReadyToUpdateSubscribers callback\", [self.logger SensitiveNSObject:[device getIdentifier]]];\n        }\n\n        [self.writerLactch await];\n\n        // take write status from peripheralManagerIsReadyToUpdateSubscribers\n        if (!success) {\n            success = self.writeStatus;\n        }\n\n        [self.writeCache release];\n        self.writeCache = nil;\n        [self.writerLactch release];\n        self.writerLactch = nil;\n        [toWrite release];\n\n        // this time write failed, don't continue\n        if (!success) {\n            break ;\n        }\n\n        offset += toWriteLen;\n    }\n\n    [self.logger d:@\"writeAndNotify: device=%@: success=%d\", [self.logger SensitiveNSObject:[device getIdentifier]], success];\n    return success;\n}\n\n#pragma mark - BertyDevice dict helper\n\n- (BertyDevice *)findPeripheral:(CBPeripheral *)peripheral {\n    BertyDevice *result = nil;\n\n    @synchronized (self.bDevices) {\n        for (BertyDevice *bDevice in self.bDevices) {\n            if (bDevice.peripheral == peripheral) {\n                result = bDevice;\n                break;\n            }\n        }\n    }\n\n    return result;\n}\n\n- (BertyDevice *)findPeripheralFromName:(NSString *) name {\n    @synchronized (self.bDevices) {\n        for (BertyDevice *bDevice in self.bDevices) {\n            if ([bDevice.name isEqualToString:name]) {\n                return bDevice;\n            }\n        }\n    }\n\n    return nil;\n}\n\n- (BertyDevice *)findPeripheralFromPID:(NSString *)peerID {\n    BertyDevice *result = nil;\n\n    @synchronized (self.bDevices) {\n        for (BertyDevice *bDevice in self.bDevices) {\n            if ([bDevice.remotePeerID isEqualToString:peerID]) {\n                result = bDevice;\n                break;\n            }\n        }\n    }\n\n    return result;\n}\n\n\n- (BertyDevice *)findPeripheralFromIdentifier:(NSUUID *)identifier {\n    BertyDevice *result = nil;\n    NSString *id = [identifier UUIDString];\n\n    @synchronized (self.bDevices) {\n        for (BertyDevice *bDevice in self.bDevices) {\n            if ([bDevice.clientSideIdentifier isEqual:id]) {\n                result = bDevice;\n                break;\n            } else if ([bDevice.serverSideIdentifier isEqual:id]) {\n                result = bDevice;\n                break;\n            }\n        }\n    }\n\n    return result;\n}\n\n#pragma mark - CentraManagerDelegate\n\n- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {\n    [self.logger i:@\"didConnectPeripheral called: device=%@\", [self.logger SensitiveNSObject:[peripheral.identifier UUIDString]]];\n\n    BertyDevice *bDevice = [self findPeripheral:peripheral];\n    if (bDevice == nil) {\n        [self.logger e:@\"didConnectPeripheral error: device=%@ not found\", [self.logger SensitiveNSObject:[peripheral.identifier UUIDString]]];\n        [self.cManager cancelPeripheralConnection:peripheral];\n        return ;\n    }\n    [bDevice handleConnect:nil];\n}\n\n- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {\n    [self.logger i:@\"didFailToConnectPeripheral called: device=%@\", [self.logger SensitiveNSObject:[peripheral.identifier UUIDString]]];\n\n    BertyDevice *bDevice = [self findPeripheral:peripheral];\n    if (bDevice == nil) {\n        [self.logger e:@\"didFailToConnectPeripheral error: device=%@ not found\", [self.logger SensitiveNSObject:[peripheral.identifier UUIDString]]];\n        return ;\n    }\n    [bDevice handleConnect:error];\n}\n\n- (void)centralManager:(CBCentralManager *)central\n didDiscoverPeripheral:(CBPeripheral *)peripheral\n     advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI {\n    NSString *id = nil;\n\n    if (advertisementData && [advertisementData.allKeys containsObject:CBAdvertisementDataLocalNameKey]) {\n        // between 2 iOS\n        id = [advertisementData valueForKeyPath:CBAdvertisementDataLocalNameKey];\n\n    } else if (advertisementData && [advertisementData.allKeys containsObject:CBAdvertisementDataServiceDataKey]) {\n        // between Android / iOS\n        NSDictionary<CBUUID *, NSData *> *data = [advertisementData valueForKey:CBAdvertisementDataServiceDataKey];\n        if (data) {\n            CBUUID *uuid = [CBUUID UUIDWithString:@\"4240\"];\n            id = [[NSString alloc] initWithData:[data objectForKey:uuid] encoding:NSUTF8StringEncoding];\n            [id autorelease];\n        } else {\n            [self.logger d:@\"didDiscoverPeripheral error: device=%@: CBAdvertisementDataServiceDataKey doesn't contains any data\", [self.logger SensitiveNSObject:[peripheral.identifier UUIDString]]];\n            return ;\n        }\n    } else {\n        // verbose\n//        [self.logger e:@\"didDiscoverPeripheral error: device=%@ has not advertisement name\", [self.logger SensitiveNSObject:[peripheral.identifier UUIDString]]];\n        return ;\n    }\n\n    if ([id length] == 0) {\n        [self.logger d:@\"didDiscoverPeripheral error: device=%@: id is empty\", [self.logger SensitiveNSObject:[peripheral.identifier UUIDString]]];\n        return ;\n    }\n\n    // only lower id can be client\n    if ([self.ID compare:id] != NSOrderedAscending) {\n        // Verbose\n//        [self.logger d:@\"didDiscoverPeripheral: device=%@: greater ID, cancel client connection\", [self.logger SensitiveNSObject:[peripheral.identifier UUIDString]]];\n        return ;\n    }\n\n    BertyDevice *nDevice = [self findPeripheralFromIdentifier:peripheral.identifier];\n    if (nDevice != nil) { // peripheral already known\n        if (nDevice.clientSideIdentifier != nil) { // peripheral already discovered\n            return ;\n        }\n        // peripheral already known by CBPeripheralManager (advertising)\n        // adding info given by CBCentralManager (scanning)\n        [self.logger d:@\"didDiscoverPeripheral: device=%@ id=%@: already known\", [self.logger SensitiveNSObject:[peripheral.identifier UUIDString]], [self.logger SensitiveNSObject:id]];\n        nDevice.peripheral = peripheral;\n        nDevice.clientSideIdentifier = [peripheral.identifier UUIDString];\n    } else {\n        nDevice = [self findPeripheralFromName:id];\n\n        if (nDevice != nil && nDevice.peripheral != nil) { // device already known with another peripheral object\n            return ;\n        }\n\n        // TODO: retest if bDevices is still null after @synchronized\n        @synchronized (self.bDevices) {\n            nDevice = [[BertyDevice alloc]initWithPeripheral:peripheral logger:self.logger central:self withName:id];\n            [self.bDevices addObject:nDevice];\n            [nDevice release];\n            [self.logger d:@\"didDiscoverPeripheral: device=%@ added to BleManager.bDevices\", [self.logger SensitiveNSObject:[peripheral.identifier UUIDString]]];\n        }\n    }\n    [self.logger d:@\"didDiscoverPeripheral: device=%@ id=%@: found. Going to connect.\", [self.logger SensitiveNSObject:[peripheral.identifier UUIDString]], [self.logger SensitiveNSObject:id]];\n    [nDevice connectWithOptions:nil];\n}\n\n- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {\n    [self.logger i:@\"didDisconnectPeripheral called: device=%@ error=%@\", [self.logger SensitiveNSObject:[peripheral.identifier UUIDString]], error];\n\n    BertyDevice *device = [self findPeripheral:peripheral];\n    if (device != nil) {\n        [device closeBertyDevice];\n        @synchronized (self.bDevices) {\n            [self.bDevices removeObject:device];\n        }\n    }\n}\n\n#pragma mark - State Management\n\n- (void)peripheralManagerDidUpdateState:(nonnull CBPeripheralManager *)peripheral {\n    NSString *stateString = nil;\n    @synchronized (self.pManager) {\n        self.pmEnable = FALSE;\n    }\n\n    switch(peripheral.state)\n    {\n        case CBManagerStateUnknown: {\n            stateString = @\"CBManagerStateUnknown\";\n            break;\n        }\n        case CBManagerStateResetting: {\n            stateString = @\"CBManagerStateResetting\";\n            break;\n        }\n        case CBManagerStateUnsupported: {\n            stateString = @\"CBManagerStateUnsupported\";\n            break;\n        }\n        case CBManagerStateUnauthorized: {\n            stateString = @\"CBManagerStateUnauthorized\";\n            break;\n        }\n        case CBManagerStatePoweredOff: {\n            stateString = @\"CBManagerStatePoweredOff\";\n            break;\n        }\n        case CBManagerStatePoweredOn: {\n            stateString = @\"CBManagerStatePoweredOn\";\n            @synchronized (self.pManager) {\n                self.pmEnable = TRUE;\n            }\n            break;\n        }\n        default: {\n            stateString = @\"State unknown, update imminent.\";\n            break;\n        }\n    }\n    [self.logger i:@\"peripheralManagerDidUpdateState: %@\", stateString];\n    [self.bleOn countDown];\n}\n\n- (void)centralManagerDidUpdateState:(nonnull CBCentralManager *)central {\n    NSString *stateString = nil;\n    @synchronized (self.cManager) {\n        self.cmEnable = FALSE;\n    }\n\n    switch(central.state)\n    {\n        case CBManagerStateResetting: {\n            break;\n        }\n        case CBManagerStateUnsupported: {\n            break;\n        }\n        case CBManagerStateUnauthorized: {\n            break;\n        }\n        case CBManagerStatePoweredOff: {\n            stateString = @\"Bluetooth is currently powered off.\";\n            break;\n        }\n        case CBManagerStatePoweredOn: {\n            stateString = @\"Bluetooth is currently powered on and available to use.\";\n            @synchronized (self.cManager) {\n                self.cmEnable = TRUE;\n            }\n            break;\n        }\n        default: {\n            stateString = @\"State unknown, update imminent.\";\n            break;\n        }\n    }\n    [self.logger i:@\"centralManagerDidUpdateState: %@\", stateString];\n    [self.bleOn countDown];\n}\n\n- (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic {\n    [self.logger d:@\"peripheralManager didSubscribeToCharacteristic called: device=%@\", [self.logger SensitiveNSObject:[central.identifier UUIDString]]];\n\n    BertyDevice *device;\n    if ((device = [self findPeripheralFromIdentifier:central.identifier]) == nil) {\n        [self.logger e:@\"peripheralManager didSubscribeToCharacteristic error: device=%@ not found\", [self.logger SensitiveNSObject:[central.identifier UUIDString]]];\n        return ;\n    }\n\n    device.cbCentral = central;\n\n    // Server doesn't know if the L2CAP handshake failed on the client side\n    // so we have to set it manually at this step.\n    device.l2capServerHandshakeRunning = FALSE;\n\n    // complete handshake\n    device.peer = [self.peerManager registerDevice:device withPeerID:device.remotePeerID isClient:FALSE];\n    if (device.peer == nil) {\n        [self.logger e:@\"peripheralManager didSubscribeToCharacteristic error: device=%@: registerDevice failed\", [self.logger SensitiveNSObject:[central.identifier UUIDString]]];\n        return ;\n    } else {\n        [self.logger d:@\"peripheralManager didSubscribeToCharacteristic: device=%@: registerDevice successed\", [self.logger SensitiveNSObject:[central.identifier UUIDString]]];\n    }\n}\n\n// server disconnection callback entry point\n- (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic {\n    [self.logger d:@\"peripheralManager didUnsubscribeFromCharacteristic called: device=%@\", [self.logger SensitiveNSObject:[central.identifier UUIDString]]];\n\n    BertyDevice *device;\n    if ((device = [self findPeripheralFromIdentifier:central.identifier]) == nil) {\n        [self.logger e:@\"peripheralManager didUnsubscribeFromCharacteristic error: device=%@ not found\", [self.logger SensitiveNSObject:[central.identifier UUIDString]]];\n        return ;\n    }\n\n    [device closeBertyDevice];\n    @synchronized (self.bDevices) {\n        [self.bDevices removeObject:device];\n    }\n}\n\n- (void)peripheralManagerIsReadyToUpdateSubscribers:(CBPeripheralManager *)peripheral {\n    [self.logger d:@\"peripheralManager peripheralManagerIsReadyToUpdateSubscribers called\"];\n\n    self.writeStatus = FALSE;\n\n    @synchronized(self.writeCache) {\n        if (self.writeCache != nil) {\n            if (self.logger.showSensitiveData) {\n                [self.logger d:@\"peripheralManagerIsReadyToUpdateSubscribers: device=%@ base64=%@ data=%@\", [self.writeCache.device getIdentifier], [self.writeCache.data base64EncodedStringWithOptions:0], [BleManager NSDataToHex:self.writeCache.data]];\n            }\n\n            if (self.writerLactch == nil) {\n                [self.logger e:@\"peripheralManagerIsReadyToUpdateSubscribers error: writer latch is null\"];\n                return ;\n            }\n\n            if (self.writeCache.device.peer == nil) {\n                [self.logger e:@\"peripheralManagerIsReadyToUpdateSubscribers error: peer object not found\"];\n                [self.writerLactch countDown];\n                return ;\n            }\n\n            if (![self.writeCache.device.peer isServerReady]) {\n                [self.logger e:@\"peripheralManagerIsReadyToUpdateSubscribers error: server not connected\"];\n                [self.writerLactch countDown];\n                return ;\n            }\n\n            self.writeStatus = [self.pManager updateValue:self.writeCache.data forCharacteristic:self.writerCharacteristic onSubscribedCentrals:@[self.writeCache.device.cbCentral]];\n\n            if (self.writeStatus) {\n                [self.logger d:@\"peripheralManagerIsReadyToUpdateSubscribers: device=%@: data sent\", [self.logger SensitiveNSObject:[self.writeCache.device getIdentifier]]];\n                [self.writerLactch countDown];\n            } else {\n                [self.logger d:@\"peripheralManagerIsReadyToUpdateSubscribers: device=%@: operation queue is full, try later\", [self.logger SensitiveNSObject:[self.writeCache.device getIdentifier]]];\n                return ;\n            }\n        }\n    }\n}\n\n#pragma mark - read\n\n\n- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request {\n    [self.logger d:@\"didReceiveReadRequests called: device=%@ offset=%lu\", [self.logger SensitiveNSObject:[request.central.identifier UUIDString]], request.offset];\n\n    if ([request.characteristic.UUID isEqual:self.peerUUID]) {\n        [self.logger d:@\"didReceiveReadRequests: device=%@: use peerID characteristic\", [self.logger SensitiveNSObject:[request.central.identifier UUIDString]]];\n\n        BertyDevice *device = [self findPeripheralFromIdentifier:request.central.identifier];\n        if (device == nil || device.remotePeerID == nil) {\n            [self.logger e:@\"didReceiveReadRequests: device=%@: need writeRequest completed before readRequest\", [self.logger SensitiveNSObject:[request.central.identifier UUIDString]]];\n            [peripheral respondToRequest:request withResult:CBATTErrorReadNotPermitted];\n            return ;\n        }\n\n        // Write PSM to big endian\n        int psm = NSSwapHostIntToBig(self.psm);\n        NSMutableData *toSend = [[NSMutableData alloc] initWithBytes:&psm length:sizeof(psm)];\n        [toSend appendData:[self.localPID dataUsingEncoding:NSUTF8StringEncoding]];\n        request.value = toSend;\n\n        [peripheral respondToRequest:request withResult:CBATTErrorSuccess];\n        [toSend release];\n    } else {\n        [self.logger e:@\"didReceiveReadRequests: device=%@: bad characteristic requested\", [self.logger SensitiveNSObject:[request.central.identifier UUIDString]]];\n        [peripheral respondToRequest:request withResult:CBATTErrorRequestNotSupported];\n        return ;\n    }\n}\n\n\n#pragma mark - write\n\n- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray<CBATTRequest *> *)requests {\n\n    BertyDevice *device;\n    NSData *data = nil;\n\n    for (CBATTRequest *request in requests) {\n        [self.logger d:@\"didReceiveWriteRequests: device=%@ base64=%@\", [self.logger SensitiveNSObject:[request.central.identifier UUIDString]], [self.logger SensitiveNSObject:[data base64EncodedStringWithOptions:0]]];\n        if (self.logger.showSensitiveData) {\n            [BleManager printLongLog:[BleManager NSDataToHex:data]];\n        }\n\n        CBMutableCharacteristic *characteristic;\n\n        @synchronized (self.bDevices) {\n            // check if we hold a remote device of this type\n            device = [self findPeripheralFromIdentifier:request.central.identifier];\n            if (device == nil) {\n                device = [[BertyDevice alloc]initWithIdentifier:[request.central.identifier UUIDString] logger:self.logger central:self asClient:FALSE];\n                [self.bDevices addObject:device];\n                [device release];\n                [self.logger d:@\"didReceiveWriteRequests: device=%@ added to BleManager.bDevices\", [self.logger SensitiveNSObject:[request.central.identifier UUIDString]]];\n            }\n        }\n\n        if ([request.characteristic.UUID isEqual:self.writerUUID]) {\n            characteristic = self.writerCharacteristic;\n        }\n        else if ([request.characteristic.UUID isEqual:self.peerUUID]) {\n            characteristic = self.peerIDCharacteristic;\n        } else {\n            [self.logger e:@\"didReceiveWriteRequests error: device=%@: bad characteristic requested\", [self.logger SensitiveNSObject:[request.central.identifier UUIDString]]];\n            [device closeBertyDevice];\n            [peripheral respondToRequest:request withResult:CBATTErrorWriteNotPermitted];\n            return ;\n        }\n\n        data = request.value;\n\n        BOOL(^handler)(NSData *) = [device.characteristicHandlers objectForKey:[request.characteristic.UUID UUIDString]];\n        if (!handler(data)) {\n            [self.logger e:@\"didReceiveWriteRequests error: device=%@: handle failed\", [self.logger SensitiveNSObject:[request.central.identifier UUIDString]]];\n            [device closeBertyDevice];\n            [peripheral respondToRequest:request withResult:CBATTErrorWriteNotPermitted];\n        }\n\n        // Process response back\n        request.value = [self.localPID dataUsingEncoding:NSUTF8StringEncoding];\n    }\n    [peripheral respondToRequest:[requests objectAtIndex:0] withResult:CBATTErrorSuccess];\n}\n\n#pragma mark - L2cap\n\n- (void)peripheralManager:(CBPeripheralManager *)peripheral didPublishL2CAPChannel:(CBL2CAPPSM)PSM error:(NSError *)error {\n    if (error != nil) {\n        [self.logger e:@\"peripheralManager didPublishL2CAPChannel error=%@\", error];\n        return ;\n    }\n    [self.logger d:@\"peripheralManager didPublishL2CAPChannel: PSM=%hu\", PSM];\n    self.psm = PSM;\n}\n\n- (void)peripheralManager:(CBPeripheralManager *)peripheral didUnpublishL2CAPChannel:(CBL2CAPPSM)PSM error:(NSError *)error {\n    [self.logger d:@\"peripheralManager didUnpublishL2CAPChannel called\"];\n\n    self.psm = 0;\n}\n\n- (void)peripheralManager:(CBPeripheralManager *)peripheral didOpenL2CAPChannel:(CBL2CAPChannel *)channel error:(NSError *)error API_AVAILABLE(ios(11.0)) {\n    [self.logger d:@\"peripheralManager didOpenL2CAPChannel called: device=%@\", [self.logger SensitiveNSObject:[channel.peer.identifier UUIDString]]];\n\n    if (error != nil) {\n        [self.logger e:@\"peripheralManager didOpenL2CAPChannel error=%@\", error];\n        return ;\n    }\n\n    BertyDevice *device;\n    if ((device = [self findPeripheralFromIdentifier:channel.peer.identifier]) == nil) {\n        [self.logger e:@\"peripheralManager didOpenL2CAPChannel error: device=%@ not found\", [self.logger SensitiveNSObject:[channel.peer.identifier UUIDString]]];\n        return ;\n    }\n\n    device.l2capChannel = channel;\n\n    device.l2capThread = [[NSThread alloc] initWithBlock:^{\n        [self.logger d:@\"peripheralManager didOpenL2CAPChannel: device=%@: in thread\", [self.logger SensitiveNSObject:[device getIdentifier]]];\n\n        channel.inputStream.delegate = device;\n        [channel.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];\n        [channel.inputStream open];\n        channel.outputStream.delegate = device;\n        [channel.outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];\n        [channel.outputStream open];\n\n        @autoreleasepool {\n            do {\n                [[NSRunLoop currentRunLoop] run];\n            } while (device.peer != nil && [device.peer isConnected]);\n        }\n    }];\n    [device.l2capThread start];\n\n    device.l2capServerHandshakeRunning = TRUE;\n}\n\n@end\n"
  },
  {
    "path": "pkg/ble-driver/BleQueue.h",
    "content": "//\n//  BleQueue.h\n//  BertyBridgeDemo\n//\n//  Created by Rémi BARBERO on 03/05/2021.\n//\n\n#import <Foundation/Foundation.h>\n#import \"Logger.h\"\n\nNS_ASSUME_NONNULL_BEGIN\n\n#define MAX_TRIES 3\n\n@interface BleQueue : NSObject\n\n@property (nonatomic, strong, nonnull) dispatch_queue_t queue;\n@property (nonatomic, strong, nonnull) Logger *logger;\n@property (nonatomic, strong, nonnull) NSMutableArray *tasks;\n@property (readwrite) BOOL taskQueueBusy;\n@property (readwrite) BOOL isRetrying;\n@property (readwrite) int nbTries;\n@property (readwrite) int index;\n\n- (instancetype __nullable) init:(dispatch_queue_t)queue logger:(Logger *__nonnull)logger;\n- (void) add:(void (^__nonnull)(void))block withCallback:(void (^__nullable)(NSError *))callback withDelay:(long)delay;\n- (void) completedTask:(NSError *__nullable)error;\n- (void) nextTask;\n- (void) retryTask;\n- (void) clear;\n\n@end\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "pkg/ble-driver/BleQueue.m",
    "content": "// +build darwin,!noproximitytransport\n//\n//  BleQueue.m\n//  BertyBridgeDemo\n//\n//  Created by Rémi BARBERO on 03/05/2021.\n//\n\n#import \"BleQueue.h\"\n#import \"TaskDelay.h\"\n\n@implementation BleQueue\n\n- (instancetype __nullable) init:(dispatch_queue_t)queue logger:(Logger *__nonnull)logger {\n    self = [super init];\n\n    if (self) {\n        _tasks = [[NSMutableArray alloc] init];\n        _queue = [queue retain];\n        _logger = [logger retain];\n    }\n\n    return self;\n}\n\n- (void)dealloc {\n    [_tasks removeAllObjects];\n    [_tasks release];\n    [_queue release];\n    [_logger release];\n\n    [super dealloc];\n}\n\n- (void) add:(void (^__nonnull)(void))block withCallback:(void (^__nullable)(NSError *))callback withDelay:(long)delay {\n    @synchronized (self.tasks) {\n        TaskDelay *task = [[TaskDelay alloc] initWithBlock:block withCallback:callback withDelay:delay withIndex:(self.index++)];\n        [self.tasks addObject:task]; // add to the end of the array\n        [self.logger d:@\"BleQueue: added task at index=%d count=%ld\", task.index, [self.tasks count]];\n        [self nextTask];\n        [task release];\n    }\n}\n\n- (void) completedTask:(NSError *__nullable)error {\n    @synchronized (self.tasks) {\n        TaskDelay *currentTask;\n\n        if ([self.tasks count] == 0) {\n            [self.logger e:@\"BleQueue: completedTask error: no task running\"];\n            return ;\n        }\n\n        currentTask = [self.tasks objectAtIndex:0];\n        [self.logger d:@\"BleQueue: completedTask at index=%d\", currentTask.index];\n        if (currentTask.callback != nil) {\n            dispatch_async(self.queue, ^{\n                currentTask.callback(error);\n            });\n        }\n\n        self.isRetrying = FALSE;\n        self.taskQueueBusy = FALSE;\n        [self.tasks removeObjectAtIndex:0];\n        [self nextTask];\n    }\n}\n\n- (void) nextTask {\n    @synchronized (self.tasks) {\n        TaskDelay *nextTask;\n\n        if (self.taskQueueBusy) {\n            [self.logger d:@\"BleQueue: nextTask: another task is running\"];\n            return ;\n        }\n\n        if ([self.tasks count] == 0 ) {\n            [self.logger d:@\"BleQueue: nextTask error: no task queued: count=%ld\", [self.tasks count]];\n            return ;\n        }\n\n        nextTask = [self.tasks objectAtIndex:0];\n        [self.logger d:@\"BleQueue: nextTask at index=%d with delay=%ld\", nextTask.index, nextTask.delay];\n\n        self.taskQueueBusy = TRUE;\n        if (!self.isRetrying) {\n            self.nbTries = 0;\n        }\n        dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(nextTask.delay * NSEC_PER_SEC));\n        dispatch_after(popTime, self.queue, nextTask.block);\n    }\n}\n\n- (void) retryTask {\n    @synchronized (self.tasks) {\n        TaskDelay *currentTask;\n\n        self.taskQueueBusy = FALSE;\n\n        if ([self.tasks count] == 0) {\n            [self.logger e:@\"BleQueue: retryTask error: no task running\"];\n        } else {\n            currentTask = [self.tasks objectAtIndex:0];\n            if (self.nbTries >= MAX_TRIES) {\n                [self.logger d:@\"BleQueue: max number of tries reached, not retrying operation anymore for task index=%d\", currentTask.index];\n                [self.tasks removeObjectAtIndex:0];\n            } else {\n                [self.logger d:@\"BleQueue: retrying task at index=%d\", currentTask.index];\n                self.isRetrying = TRUE;\n            }\n        }\n\n        [self nextTask];\n    }\n}\n\n- (void) clear {\n    @synchronized (self.tasks) {\n        [self.tasks removeAllObjects];\n        self.taskQueueBusy = FALSE;\n    }\n}\n\n@end\n"
  },
  {
    "path": "pkg/ble-driver/CircularQueue.h",
    "content": "//\n//  CircularQueue.h\n//\n//  Created on 9/21/13.\n//\n//   https://gist.github.com/werediver/5345f91c897b8173e40e\n//\n\n#import <Foundation/Foundation.h>\n\n# define DEFAULT_CAPACITY 8\n\n@interface CircularQueue : NSObject\n\n@property (nonatomic, strong, nonnull) NSMutableArray *data;\n@property (nonatomic, assign, readwrite) NSInteger capacity;\n@property (nonatomic, assign, readwrite) NSInteger writeSequence;\n@property (nonatomic, assign, readwrite) NSInteger readSequence;\n\n- (instancetype __nonnull)initWithCapacity:(NSUInteger)capacity;\n- (void)offer:(id __nonnull)obj; // Enqueue\n- (id __nonnull)poll; // Get object and unqueue\n- (id __nonnull)element; // Get object\n- (void)clean;\n\n@end\n"
  },
  {
    "path": "pkg/ble-driver/CircularQueue.m",
    "content": "// +build darwin,!noproximitytransport\n\n#import \"CircularQueue.h\"\n\n@implementation CircularQueue\n\n- (instancetype __nonnull)initWithCapacity:(NSUInteger)capacity {\n\tself = [super init];\n\n\tif (self) {\n        _capacity = capacity < 1 ? DEFAULT_CAPACITY : capacity;\n        _data = [[NSMutableArray alloc] initWithCapacity:_capacity];\n        _writeSequence = -1;\n        _readSequence = 0;\n\n\t\tfor (NSUInteger i = 0; i < _capacity; ++i) {\n\t\t\t[_data addObject:[NSNull null]];\n\t\t}\n\t}\n\n\treturn self;\n}\n\n- (void)dealloc {\n    [_data release];\n\n    [super dealloc];\n}\n\n- (void)offer:(id)obj {\n    if ([self isNotFull]) {\n        NSInteger nextWrite = (self.writeSequence + 1) % self.capacity;\n        [self.data replaceObjectAtIndex:nextWrite withObject:obj];\n\n        self.writeSequence++;\n    } else {\n        @throw [[[NSException alloc] initWithName:NSRangeException reason:nil userInfo:nil] autorelease];\n    }\n}\n\n- (id)poll {\n    if ([self isNotEmpty]) {\n        NSInteger index = self.readSequence % self.capacity;\n        id value = [[self.data objectAtIndex:index] retain];\n\n        [self.data replaceObjectAtIndex:index withObject:[NSNull null]];\n        self.readSequence++;\n\n        return [value autorelease];\n    }\n\n    return [NSNull null];\n}\n\n- (id)element {\n    if ([self isNotEmpty]) {\n        NSInteger index = self.readSequence % self.capacity;\n        return [self.data objectAtIndex:index];\n    }\n\n    return [NSNull null];\n}\n\n- (void)clean {\n\tfor (NSUInteger i = 0; i < _capacity; ++i) {\n\t\t[self.data replaceObjectAtIndex:i withObject:[NSNull null]];\n\t}\n\tself.readSequence = 0;\n    self.writeSequence = -1;\n}\n\n- (NSInteger)size {\n    return (self.writeSequence - self.readSequence) + 1;\n}\n\n- (BOOL)isEmpty {\n    return self.writeSequence < self.readSequence;\n}\n\n- (BOOL)isFull {\n    return [self size] >= self.capacity;\n}\n\n- (BOOL)isNotEmpty {\n    return ![self isEmpty];\n}\n\n- (BOOL)isNotFull {\n    return ![self isFull];\n}\n\n@end\n"
  },
  {
    "path": "pkg/ble-driver/ConnectedPeer.h",
    "content": "//\n//  ConnectedPeer.h\n//  BertyBridgeDemo\n//\n//  Created by Rémi BARBERO on 29/04/2021.\n//\n\n#import <Foundation/Foundation.h>\n\nNS_ASSUME_NONNULL_BEGIN\n\n@class BertyDevice;\n\n@interface ConnectedPeer : NSObject\n\n@property (nonatomic, assign, nullable) BertyDevice *client;\n@property (nonatomic, assign, nullable) BertyDevice *server;\n@property (readwrite, getter=isConnected) BOOL connected;\n\n- (BOOL)isClientReady;\n- (BOOL)isServerReady;\n\n@end\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "pkg/ble-driver/ConnectedPeer.m",
    "content": "// +build darwin,!noproximitytransport\n//\n//  ConnectedPeer.m\n//  BertyBridgeDemo\n//\n//  Created by Rémi BARBERO on 29/04/2021.\n//\n\n#import \"ConnectedPeer.h\"\n\n@implementation ConnectedPeer\n\n- (BOOL)isClientReady {\n    return self.client != nil;\n}\n\n- (BOOL)isServerReady {\n    return self.server != nil;\n}\n\n@end\n"
  },
  {
    "path": "pkg/ble-driver/CountDownLatch_darwin.h",
    "content": "//\n//  CountDownLatch.h\n//  ble\n//\n//  Created by sacha on 22/11/2018.\n//  Copyright © 2018 berty. All rights reserved.\n//\n\n#import <Foundation/Foundation.h>\n\n#ifndef CountDownLatch_h\n#define CountDownLatch_h\n\n@interface CountDownLatch : NSObject\n\n@property (nonatomic, assign, readwrite) NSInteger count;\n@property (atomic, strong, readwrite) dispatch_semaphore_t semaphore;\n@property (nonatomic, strong) dispatch_queue_t dispatch_queue;\n@property (readwrite) BOOL timeout;\n\n- (instancetype)initCount:(NSInteger)count;\n- (void)incrementCount;\n- (void)countDown;\n- (void)await;\n- (void)await:(NSUInteger)timeout withCancelBlock:(void (^)(void))callback;\n\n@end\n\n#endif\n"
  },
  {
    "path": "pkg/ble-driver/CountDownLatch_darwin.m",
    "content": "// +build darwin,!noproximitytransport\n//\n//  CountDownLatch.m\n//  ble\n//\n//  Created by sacha on 22/11/2018.\n//  Copyright © 2018 berty. All rights reserved.\n//\n\n#import \"CountDownLatch_darwin.h\"\n\n@implementation CountDownLatch\n\n- (instancetype)initCount:(NSInteger)count {\n    if (count < 0) {\n        return nil;\n    }\n\n    self = [super self];\n\n    if (self) {\n        _count = count;\n        _semaphore = dispatch_semaphore_create(0);\n        _dispatch_queue = dispatch_queue_create(\"CountDownLatchQueue\", DISPATCH_QUEUE_SERIAL);\n    }\n\n    return self;\n}\n\n- (void)dealloc {\n    _semaphore = nil;\n    dispatch_release(_dispatch_queue);\n    _dispatch_queue = nil;\n\n    [super dealloc];\n}\n\n- (void)incrementCount {\n    dispatch_async(self.dispatch_queue, ^{\n        self.count++;\n    });\n}\n\n- (void)countDown {\n    dispatch_async(self.dispatch_queue, ^{\n        self.count--;\n        if (self.count == 0) {\n            dispatch_semaphore_signal(self.semaphore);\n        }\n    });\n}\n\n- (void)await {\n    dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);\n}\n\n- (void)await:(NSUInteger)timeout withCancelBlock:(void (^)(void))callback {\n    self.timeout = TRUE;\n    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeout * NSEC_PER_SEC));\n    dispatch_after(popTime, self.dispatch_queue, ^(void){\n        if (self.timeout) {\n            callback();\n            dispatch_semaphore_signal(self.semaphore);\n        }\n    });\n    dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);\n    self.timeout = FALSE;\n}\n\n@end\n"
  },
  {
    "path": "pkg/ble-driver/Logger.h",
    "content": "//\n//  Logger.h\n//  BertyBridgeDemo\n//\n//  Created by Rémi BARBERO on 08/12/2021.\n//\n\n#import <Foundation/Foundation.h>\n#import <os/log.h>\n\nNS_ASSUME_NONNULL_BEGIN\n\n#define SENSITIVE_MASK @\"####\"\n\n// Log levels\ntypedef NS_ENUM(uint8_t, level) {\n    Debug,\n    Info,\n    Warn,\n    Error,\n};\n\n@interface BLE_Logger : NSObject\n\n@property (nonatomic, strong, nonnull) os_log_t logger;\n@property (readwrite) BOOL showSensitiveData;\n@property (readwrite) BOOL useExternalLogger;\n\n- (instancetype __nonnull)initLocalLoggerWithSubSystem:(const char *)subSystem andCategorie:(const char*)categorie showSensitiveData:(BOOL)showSensitiveData;\n- (instancetype __nonnull)initWithExternalLoggerAndShowSensitiveData:(BOOL)showSensitiveData;\n- (void)log:(enum level)level withFormat:(NSString *__nonnull)format withArgs:(va_list)args;\n- (void)d:(NSString *__nonnull)format, ...;\n- (void)i:(NSString *__nonnull)format, ...;\n- (void)e:(NSString *__nonnull)format, ...;\n- (BOOL)showSensitiveData;\n- (BOOL)useExternalLogger;\n- (NSString *__nonnull)SensitiveNSObject:(id __nonnull)data;\n- (NSString *__nonnull)SensitiveString:(const char *)data;\n\n@end\n\n@compatibility_alias Logger BLE_Logger;\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "pkg/ble-driver/Logger.m",
    "content": "// +build darwin,!noproximitytransport\n//\n//  Logger.m\n//  BertyBridgeDemo\n//\n//  Created by Rémi BARBERO on 08/12/2021.\n//\n\n#import <os/log.h>\n#import \"Logger.h\"\n#import \"BleInterface_darwin.h\"\n\n@implementation Logger\n\n- (instancetype __nonnull)initLocalLoggerWithSubSystem:(const char *)subSystem andCategorie:(const char*)categorie showSensitiveData:(BOOL)showSensitiveData {\n    self = [super init];\n\n    if (self) {\n        _logger = os_log_create(subSystem, categorie);\n        _useExternalLogger = FALSE;\n        _showSensitiveData = showSensitiveData;\n    }\n\n    return self;\n}\n\n- (instancetype __nonnull)initWithExternalLoggerAndShowSensitiveData:(BOOL)showSensitiveData {\n    self = [super init];\n\n    if (self) {\n        _logger = nil;\n        _useExternalLogger = TRUE;\n        _showSensitiveData = showSensitiveData;\n    }\n\n    return self;\n}\n\n- (void)log:(enum level)level withFormat:(NSString *__nonnull)format withArgs:(va_list)args {\n    NSString *message = [[NSString alloc] initWithFormat:format arguments:args];\n\n    if (self.useExternalLogger) {\n        BLEBridgeLog(level, message);\n    } else {\n        if (self.logger == nil) {\n            NSLog(@\"log error: logger is not set\");\n        } else {\n            uint8_t osLevel;\n            switch (level) {\n                case Debug:\n                    osLevel = OS_LOG_TYPE_DEBUG;\n                    break ;\n                case Info:\n                    osLevel = OS_LOG_TYPE_INFO;\n                    break ;\n                case Error:\n                    osLevel = OS_LOG_TYPE_ERROR;\n                    break ;\n                default:\n                    osLevel = OS_LOG_TYPE_DEFAULT;\n                    break ;\n            }\n\n            os_log_with_type(self.logger, osLevel, \"%@\", message);\n        }\n    }\n\n    [message release];\n}\n\n- (void)d:(NSString *__nonnull)format, ... {\n    va_list args;\n    va_start(args, format);\n    [self log:Debug withFormat:format withArgs:args];\n    va_end(args);\n}\n\n- (void)i:(NSString *__nonnull)format, ... {\n    va_list args;\n    va_start(args, format);\n    [self log:Info withFormat:format withArgs:args];\n    va_end(args);\n}\n\n- (void)e:(NSString *__nonnull)format, ... {\n    va_list args;\n    va_start(args, format);\n    [self log:Error withFormat:format withArgs:args];\n    va_end(args);\n}\n\n- (NSString *__nonnull)SensitiveNSObject:(id __nonnull)data {\n    if (self.showSensitiveData) {\n        return [NSString stringWithFormat:@\"%@\", data];\n    } else {\n        return SENSITIVE_MASK;\n    }\n}\n\n- (NSString *__nonnull)SensitiveString:(const char *)data {\n    if (data == nil) {\n        return @\"\";\n    }\n\n    if (self.showSensitiveData) {\n        return [NSString stringWithFormat:@\"%s\", data];\n    } else {\n        return SENSITIVE_MASK;\n    }\n}\n\n@end\n"
  },
  {
    "path": "pkg/ble-driver/PeerManager.h",
    "content": "//\n//  PeerManager.h\n//  BertyBridgeDemo\n//\n//  Created by Rémi BARBERO on 29/04/2021.\n//\n\n#import <Foundation/Foundation.h>\n\n#import \"Logger.h\"\n#import \"ConnectedPeer.h\"\n\nNS_ASSUME_NONNULL_BEGIN\n\n@class BertyDevice;\n\n@interface PeerManager : NSObject\n\n@property (nonatomic, strong, nonnull) NSMutableDictionary *connectedPeers;\n@property (nonatomic, strong, nonnull) Logger *logger;\n\n- (instancetype __nonnull)initWithLogger:(Logger *__nonnull)logger;\n- (ConnectedPeer *__nonnull)getPeer:(NSString *__nonnull) peerID;\n- (ConnectedPeer *__nullable)registerDevice:(BertyDevice *__nonnull)device withPeerID:(NSString *__nonnull)peerID isClient:(BOOL)isClient;\n- (void)unregisterDevice:(BertyDevice *)device;\n- (void)removePeer:(NSString *__nonnull) peerID;\n- (void)removeAllPeers;\n\n@end\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "pkg/ble-driver/PeerManager.m",
    "content": "// +build darwin,!noproximitytransport\n//\n//  PeerManager.m\n//  BertyBridgeDemo\n//\n//  Created by Rémi BARBERO on 29/04/2021.\n//\n\n#import \"PeerManager.h\"\n#import \"BleInterface_darwin.h\"\n\n@implementation PeerManager\n\n- (instancetype __nonnull)initWithLogger:(Logger *__nonnull)logger {\n    self = [super init];\n\n    if (self) {\n        _logger = [logger retain];\n        _connectedPeers = [[NSMutableDictionary alloc] init];\n    }\n\n    return self;\n}\n\n- (void)dealloc {\n    [_connectedPeers release];\n    [_logger release];\n\n    [super dealloc];\n}\n\n- (ConnectedPeer *__nonnull)getPeer:(NSString *__nonnull) peerID {\n    [self.logger d:@\"getPeer called: peerID=%@\", [self.logger SensitiveNSObject:peerID]];\n\n    ConnectedPeer *peer;\n\n    @synchronized (_connectedPeers) {\n        if ((peer = [self.connectedPeers objectForKey:peerID]) != nil) {\n            [self.logger d:@\"getPeer: peerID=%@ alread created\", [self.logger SensitiveNSObject:peerID]];\n            return peer;\n        }\n\n        [self.logger d:@\"getPeer: peerID=%@ created\", [self.logger SensitiveNSObject:peerID]];\n        peer = [[ConnectedPeer alloc] init];\n        [self.connectedPeers setObject:peer forKey:peerID];\n        [peer release];\n        return peer;\n    }\n}\n\n- (ConnectedPeer *__nullable)registerDevice:(BertyDevice *__nonnull)device withPeerID:(NSString *__nonnull)peerID isClient:(BOOL)isClient {\n    [self.logger d:@\"registerDevice called: identifier=%@ peer=%@ isClient=%d\", [self.logger SensitiveNSObject:[device getIdentifier]], [self.logger SensitiveNSObject:peerID], isClient];\n\n    ConnectedPeer *peer;\n\n    @synchronized (_connectedPeers) {\n        peer = [self getPeer:peerID];\n        if (isClient) {\n            peer.client = device;\n        } else {\n            peer.server = device;\n        }\n\n        device.peer = peer;\n\n        peer.connected = TRUE;\n\n        if (!BLEBridgeHandleFoundPeer(peerID)) {\n            [self.logger e:@\"registerDevice error: device=%@ peer=%@: HandleFoundPeer failed\", [self.logger SensitiveNSObject:[device getIdentifier]], [self.logger SensitiveNSObject:peerID]];\n            return NULL;\n        }\n\n        [device flushCache];\n    }\n\n    return peer;\n}\n\n- (void)unregisterDevice:(BertyDevice *)device {\n    [self.logger d:@\"unregisterDevice called: device=%@ peerID=%@\", [self.logger SensitiveNSObject:[device getIdentifier]], [self.logger SensitiveNSObject:device.remotePeerID]];\n\n    ConnectedPeer *peer;\n\n    @synchronized (_connectedPeers) {\n        if ((peer = [self.connectedPeers objectForKey:device.remotePeerID]) == nil) {\n            [self.logger e:@\"unregisterDevice called: device=%@ peerID=%@: peerID not found\", [self.logger SensitiveNSObject:[device getIdentifier]], [self.logger SensitiveNSObject:device.remotePeerID]];\n            return ;\n        }\n\n        if ([peer isConnected]) {\n            [self.logger d:@\"unregisterDevice called: device=%{public}@ peerID=%{public}@: calling HandleLostPeer\", [self.logger SensitiveNSObject:[device getIdentifier]], [self.logger SensitiveNSObject:device.remotePeerID]];\n            BLEBridgeHandleLostPeer(device.remotePeerID);\n            peer.connected = FALSE;\n        }\n\n        [self removePeer:device.remotePeerID];\n    }\n}\n\n- (void)removePeer:(NSString *__nonnull) peerID {\n    [self.logger d:@\"removePeer called: peerID=%{public}@\", [self.logger SensitiveNSObject:peerID]];\n\n    @synchronized (_connectedPeers) {\n            [self.connectedPeers removeObjectForKey:peerID];\n    }\n}\n\n- (void)removeAllPeers {\n    [self.logger d:@\"removeAllPeers called\"];\n\n    @synchronized (_connectedPeers) {\n            [self.connectedPeers removeAllObjects];\n    }\n}\n\n@end\n"
  },
  {
    "path": "pkg/ble-driver/TaskDelay.h",
    "content": "//\n//  TaskDelay.h\n//  BertyBridgeDemo\n//\n//  Created by Rémi BARBERO on 03/05/2021.\n//\n\n#import <Foundation/Foundation.h>\n\nNS_ASSUME_NONNULL_BEGIN\n\n@interface TaskDelay : NSObject\n\n@property (nonatomic, copy) void (^block)(void);\n@property (nonatomic, copy) void (^callback)(NSError *);\n@property (nonatomic, assign) long delay;\n@property (nonatomic, assign) int index;\n\n- (instancetype __nullable) initWithBlock:(void (^ __nonnull)(void))block withCallback:(void (^__nullable)(NSError *))callback withDelay:(long)delay withIndex:(int)index;\n\n@end\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "pkg/ble-driver/TaskDelay.m",
    "content": "// +build darwin,!noproximitytransport\n//\n//  TaskDelay.m\n//  BertyBridgeDemo\n//\n//  Created by Rémi BARBERO on 03/05/2021.\n//\n\n#import \"TaskDelay.h\"\n\n@implementation TaskDelay\n\n- (instancetype __nullable) initWithBlock:(void (^ __nonnull)(void))block withCallback:(void (^__nullable)(NSError *))callback withDelay:(long)delay withIndex:(int)index {\n    self = [super init];\n\n    if (self) {\n        _block = [block copy];\n        _callback = [callback copy];\n        _delay = delay;\n        _index = index;\n    }\n\n    return self;\n}\n\n- (void)dealloc {\n    [_block release];\n    [_callback release];\n    [super dealloc];\n}\n\n@end\n"
  },
  {
    "path": "pkg/ble-driver/WriteDataCache.h",
    "content": "//\n//  WriteDataCache.h\n//  BertyBridgeDemo\n//\n//  Created by Rémi BARBERO on 03/08/2021.\n//\n\n#import <Foundation/Foundation.h>\n\nNS_ASSUME_NONNULL_BEGIN\n\n@class BertyDevice;\n\n@interface WriteDataCache : NSObject\n\n@property (nonatomic, strong, nonnull) BertyDevice *device;\n@property (nonatomic, strong, nonnull) NSData *data;\n\n- (instancetype __nonnull) initWithDevice:(BertyDevice *__nonnull)device withData:(NSData *__nonnull)data;\n\n@end\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "pkg/ble-driver/WriteDataCache.m",
    "content": "// +build darwin,!noproximitytransport\n//\n//  WriteDataCache.m\n//  BertyBridgeDemo\n//\n//  Created by Rémi BARBERO on 03/08/2021.\n//\n\n#import \"WriteDataCache.h\"\n#import \"BertyDevice_darwin.h\"\n\n@implementation WriteDataCache\n\n- (instancetype __nonnull) initWithDevice:(BertyDevice *__nonnull)device withData:(NSData *__nonnull)data {\n    self = [super init];\n\n    if (self) {\n        _device = [device retain];\n        _data = [data retain];\n    }\n\n    return self;\n}\n\n- (void)dealloc {\n    [_device release];\n    [_data release];\n\n    [super dealloc];\n}\n\n@end\n"
  },
  {
    "path": "pkg/ble-driver/bridge_android.go",
    "content": "//go:build android && !noproximitytransport\n\npackage ble\n\nimport (\n\t\"go.uber.org/zap\"\n\n\tproximity \"berty.tech/weshnet/v2/pkg/proximitytransport\"\n)\n\n// Supported is used by main package as default value for enable the BLE  driver.\n// While UI actually enable or not the Java BLE driver.\n// TODO: remove this when UI will be able to handle this for the first App launching.\nconst Supported = true\n\n// Noop implementation for Android\n// Real driver is given from Java directly here: berty/js/android/app/src/main/java/tech/berty/gobridge/bledriver\nfunc NewDriver(logger *zap.Logger) proximity.ProximityDriver {\n\tlogger = logger.Named(\"BLE\")\n\tlogger.Info(\"NewDriver(): Java driver not found\")\n\n\treturn proximity.NewNoopProximityDriver(ProtocolCode, ProtocolName, DefaultAddr)\n}\n"
  },
  {
    "path": "pkg/ble-driver/bridge_darwin.go",
    "content": "//go:build darwin && cgo && !noproximitytransport\n// +build darwin,cgo,!noproximitytransport\n\npackage ble\n\n/*\n#cgo CFLAGS: -x objective-c -fno-objc-arc\n#cgo darwin LDFLAGS: -framework Foundation -framework CoreBluetooth\n#include <stdlib.h>\n#import \"BleInterface_darwin.h\"\n#import \"Logger.h\"\n*/\nimport \"C\"\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"unsafe\"\n\n\t\"go.uber.org/zap\"\n\n\tproximity \"berty.tech/weshnet/v2/pkg/proximitytransport\"\n)\n\nconst Supported = true\n\nvar gLogger *zap.Logger\n\ntype Driver struct {\n\tprotocolCode int\n\tprotocolName string\n\tdefaultAddr  string\n}\n\n// Driver is a proximity.ProximityDriver\nvar _ proximity.ProximityDriver = (*Driver)(nil)\n\nfunc NewDriver(logger *zap.Logger) proximity.ProximityDriver {\n\tif logger == nil {\n\t\tlogger = zap.NewNop()\n\t} else {\n\t\tlogger = logger.Named(\"BLE\")\n\t\tlogger.Debug(\"NewDriver()\")\n\t\tC.BLEUseExternalLogger()\n\t}\n\tgLogger = logger\n\n\treturn &Driver{\n\t\tprotocolCode: ProtocolCode,\n\t\tprotocolName: ProtocolName,\n\t\tdefaultAddr:  DefaultAddr,\n\t}\n}\n\n//export BLEHandleFoundPeer\nfunc BLEHandleFoundPeer(remotePID *C.char) int { // nolint:revive // Need to prefix func name to avoid duplicate symbols between proximity drivers\n\tgoPID := C.GoString(remotePID)\n\n\tproximity.TransportMapMutex.RLock()\n\tt, ok := proximity.TransportMap[ProtocolName]\n\tproximity.TransportMapMutex.RUnlock()\n\tif !ok {\n\t\treturn 0\n\t}\n\tif t.HandleFoundPeer(goPID) {\n\t\treturn 1\n\t}\n\treturn 0\n}\n\n//export BLEHandleLostPeer\nfunc BLEHandleLostPeer(remotePID *C.char) { // nolint:revive // Need to prefix func name to avoid duplicate symbols between proximity drivers\n\tgoPID := C.GoString(remotePID)\n\n\tproximity.TransportMapMutex.RLock()\n\tt, ok := proximity.TransportMap[ProtocolName]\n\tproximity.TransportMapMutex.RUnlock()\n\tif !ok {\n\t\treturn\n\t}\n\tt.HandleLostPeer(goPID)\n}\n\n//export BLEReceiveFromPeer\nfunc BLEReceiveFromPeer(remotePID *C.char, payload unsafe.Pointer, length C.int) { // nolint:revive // Need to prefix func name to avoid duplicate symbols between proximity drivers\n\tgoPID := C.GoString(remotePID)\n\tgoPayload := C.GoBytes(payload, length)\n\n\tproximity.TransportMapMutex.RLock()\n\tt, ok := proximity.TransportMap[ProtocolName]\n\tproximity.TransportMapMutex.RUnlock()\n\tif !ok {\n\t\treturn\n\t}\n\tt.ReceiveFromPeer(goPID, goPayload)\n}\n\n//export BLELog\nfunc BLELog(level C.enum_level, message *C.char) { //nolint:revive\n\tif gLogger == nil {\n\t\tfmt.Fprintf(os.Stderr, \"logger not found\\n\")\n\t\treturn\n\t}\n\n\tgoMessage := C.GoString(message)\n\tswitch level {\n\tcase C.Debug:\n\t\tgLogger.Debug(goMessage)\n\tcase C.Info:\n\t\tgLogger.Info(goMessage)\n\tcase C.Warn:\n\t\tgLogger.Warn(goMessage)\n\tcase C.Error:\n\t\tgLogger.Error(goMessage)\n\t}\n}\n\nfunc (d *Driver) Start(localPID string) {\n\tcPID := C.CString(localPID)\n\tdefer C.free(unsafe.Pointer(cPID))\n\n\tC.BLEStart(cPID)\n}\n\nfunc (d *Driver) Stop() {\n\tC.BLEStop()\n}\n\nfunc (d *Driver) DialPeer(remotePID string) bool {\n\tcPID := C.CString(remotePID)\n\tdefer C.free(unsafe.Pointer(cPID))\n\n\treturn C.BLEDialPeer(cPID) == 1\n}\n\nfunc (d *Driver) SendToPeer(remotePID string, payload []byte) bool {\n\tcPID := C.CString(remotePID)\n\tdefer C.free(unsafe.Pointer(cPID))\n\tcPayload := C.CBytes(payload)\n\tdefer C.free(cPayload)\n\n\treturn C.BLESendToPeer(cPID, cPayload, C.int(len(payload))) == 1\n}\n\nfunc (d *Driver) CloseConnWithPeer(remotePID string) {\n\tcPID := C.CString(remotePID)\n\tdefer C.free(unsafe.Pointer(cPID))\n\n\tC.BLECloseConnWithPeer(cPID)\n}\n\nfunc (d *Driver) ProtocolCode() int {\n\treturn d.protocolCode\n}\n\nfunc (d *Driver) ProtocolName() string {\n\treturn d.protocolName\n}\n\nfunc (d *Driver) DefaultAddr() string {\n\treturn d.defaultAddr\n}\n"
  },
  {
    "path": "pkg/ble-driver/bridge_unsupported.go",
    "content": "//go:build (!darwin && !android) || noproximitytransport\n\npackage ble\n\nimport (\n\t\"go.uber.org/zap\"\n\n\tproximity \"berty.tech/weshnet/v2/pkg/proximitytransport\"\n)\n\nconst Supported = false\n\n// Noop implementation for platform that are not Darwin\n\nfunc NewDriver(logger *zap.Logger) proximity.ProximityDriver {\n\tlogger = logger.Named(\"BLE\")\n\tlogger.Info(\"NewDriver(): incompatible system\")\n\n\treturn proximity.NewNoopProximityDriver(ProtocolCode, ProtocolName, DefaultAddr)\n}\n"
  },
  {
    "path": "pkg/ble-driver/const.go",
    "content": "package ble\n\nconst (\n\tDefaultAddr  = \"/ble/Qmeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\"\n\tProtocolCode = 0x0042\n\tProtocolName = \"ble\"\n)\n"
  },
  {
    "path": "pkg/ble-driver/example_test.go",
    "content": "package ble_test\n"
  },
  {
    "path": "pkg/ble-driver/init.go",
    "content": "package ble\n\nimport (\n\tma \"github.com/multiformats/go-multiaddr\"\n)\n\nfunc init() { // nolint:gochecknoinits\n\terr := ma.AddProtocol(newProtocol())\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n"
  },
  {
    "path": "pkg/ble-driver/multiaddr.go",
    "content": "package ble\n\nimport (\n\tpeer \"github.com/libp2p/go-libp2p/core/peer\"\n\tma \"github.com/multiformats/go-multiaddr\"\n)\n\nfunc newProtocol() ma.Protocol {\n\ttranscoderMC := ma.NewTranscoderFromFunctions(mcStB, mcBtS, mcVal)\n\treturn ma.Protocol{\n\t\tName:       ProtocolName,\n\t\tCode:       ProtocolCode,\n\t\tVCode:      ma.CodeToVarint(ProtocolCode),\n\t\tSize:       -1,\n\t\tPath:       false,\n\t\tTranscoder: transcoderMC,\n\t}\n}\n\nfunc mcStB(s string) ([]byte, error) {\n\t_, err := peer.Decode(s)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn []byte(s), nil\n}\n\nfunc mcBtS(b []byte) (string, error) {\n\t_, err := peer.Decode(string(b))\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn string(b), nil\n}\n\nfunc mcVal(b []byte) error {\n\t_, err := peer.Decode(string(b))\n\treturn err\n}\n"
  },
  {
    "path": "pkg/cryptoutil/cryptoutil.go",
    "content": "package cryptoutil\n\nimport (\n\t\"crypto/aes\"\n\t\"crypto/cipher\"\n\tcrand \"crypto/rand\"\n\t\"crypto/sha256\"\n\t\"crypto/sha512\"\n\t\"fmt\"\n\n\t\"filippo.io/edwards25519\"\n\t\"github.com/libp2p/go-libp2p/core/crypto\"\n\tpb \"github.com/libp2p/go-libp2p/core/crypto/pb\"\n\t\"golang.org/x/crypto/ed25519\"\n\t\"golang.org/x/crypto/scrypt\"\n\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n)\n\nconst (\n\tKeySize          = 32 // Key size required by box\n\tNonceSize        = 24 // Nonce size required by box\n\tScryptIterations = 1 << 15\n\tScryptR          = 8\n\tScryptP          = 1\n\tScryptKeyLen     = 32\n)\n\nfunc ConcatAndHashSha256(slices ...[]byte) *[sha256.Size]byte {\n\tvar concat []byte\n\n\tfor _, slice := range slices {\n\t\tconcat = append(concat, slice...)\n\t}\n\tchecksum := sha256.Sum256(concat)\n\n\treturn &checksum\n}\n\nfunc GenerateNonce() (*[NonceSize]byte, error) {\n\tvar nonce [NonceSize]byte\n\n\tsize, err := crand.Read(nonce[:])\n\tif size != NonceSize {\n\t\terr = fmt.Errorf(\"size read: %d (required %d)\", size, NonceSize)\n\t}\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrCryptoRandomGeneration.Wrap(err)\n\t}\n\n\treturn &nonce, nil\n}\n\nfunc GenerateNonceSize(size int) ([]byte, error) {\n\tnonce := make([]byte, size)\n\n\treadSize, err := crand.Read(nonce)\n\tif readSize != size {\n\t\terr = fmt.Errorf(\"size read: %d (required %d)\", readSize, size)\n\t}\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrCryptoRandomGeneration.Wrap(err)\n\t}\n\n\treturn nonce, nil\n}\n\nfunc NonceSliceToArray(nonceSlice []byte) (*[NonceSize]byte, error) {\n\tvar nonceArray [NonceSize]byte\n\n\tif l := len(nonceSlice); l != NonceSize {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"invalid nonce size, expected %d bytes, got %d\", NonceSize, l))\n\t}\n\tcopy(nonceArray[:], nonceSlice)\n\n\treturn &nonceArray, nil\n}\n\nfunc KeySliceToArray(keySlice []byte) (*[KeySize]byte, error) {\n\tvar keyArray [KeySize]byte\n\n\tif l := len(keySlice); l != KeySize {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"unable to convert slice to array, unexpected slice size: %d (expected %d)\", l, KeySize))\n\t}\n\tcopy(keyArray[:], keySlice)\n\n\treturn &keyArray, nil\n}\n\nfunc SeedFromEd25519PrivateKey(key crypto.PrivKey) ([]byte, error) {\n\t// Similar to (*ed25519).Seed()\n\tif key.Type() != pb.KeyType_Ed25519 {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput\n\t}\n\n\tr, err := key.Raw()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\tif len(r) != ed25519.PrivateKeySize {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput\n\t}\n\n\treturn r[:ed25519.PrivateKeySize-ed25519.PublicKeySize], nil\n}\n\n// EdwardsToMontgomery converts ed25519 priv/pub keys to X25519 keys.\nfunc EdwardsToMontgomery(privKey crypto.PrivKey, pubKey crypto.PubKey) (*[32]byte, *[32]byte, error) {\n\tmongPriv, err := EdwardsToMontgomeryPriv(privKey)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tmongPub, err := EdwardsToMontgomeryPub(pubKey)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\treturn mongPriv, mongPub, nil\n}\n\n// EdwardsToMontgomeryPub converts ed25519 pub key to X25519 pub key.\nfunc EdwardsToMontgomeryPub(pubKey crypto.PubKey) (*[KeySize]byte, error) {\n\tvar mongPub [KeySize]byte\n\n\tif pubKey.Type() != pb.KeyType_Ed25519 {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput\n\t}\n\n\trawPublicKey, err := pubKey.Raw()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to get raw public key: %w\", err)\n\t} else if len(rawPublicKey) != ed25519.PublicKeySize {\n\t\treturn nil, fmt.Errorf(\"invalid ed25519 public key size: %w\", err)\n\t}\n\n\terr = PublicKeyToCurve25519(&mongPub, (ed25519.PublicKey)(rawPublicKey))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to get publickey to curve 25519: %w\", err)\n\t}\n\n\treturn &mongPub, nil\n}\n\n// EdwardsToMontgomeryPriv converts ed25519 priv key to X25519 priv key.\nfunc EdwardsToMontgomeryPriv(privKey crypto.PrivKey) (*[KeySize]byte, error) {\n\tvar mongPriv [KeySize]byte\n\n\tif privKey.Type() != pb.KeyType_Ed25519 {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput\n\t}\n\n\trawPrivateKey, err := privKey.Raw()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t} else if len(rawPrivateKey) != ed25519.PrivateKeySize {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput\n\t}\n\n\tPrivateKeyToCurve25519(&mongPriv, rawPrivateKey)\n\n\treturn &mongPriv, nil\n}\n\n// AESGCMEncrypt use AES+GCM to encrypt plaintext data.\n//\n// The generated output will be longer than the original plaintext input.\nfunc AESGCMEncrypt(key, data []byte) ([]byte, error) {\n\tblockCipher, err := aes.NewCipher(key)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tgcm, err := cipher.NewGCM(blockCipher)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tnonce, err := GenerateNonceSize(gcm.NonceSize())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tciphertext := gcm.Seal(nonce, nonce, data, nil)\n\n\treturn ciphertext, nil\n}\n\n// AESGCMDecrypt uses AES+GCM to decrypt plaintext data.\nfunc AESGCMDecrypt(key, data []byte) ([]byte, error) {\n\tblockCipher, err := aes.NewCipher(key)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tgcm, err := cipher.NewGCM(blockCipher)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tnonce, ciphertext := data[:gcm.NonceSize()], data[gcm.NonceSize():]\n\n\tplaintext, err := gcm.Open(nil, nonce, ciphertext, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn plaintext, nil\n}\n\n// AESCTRStream returns a CTR stream that can be used to produce ciphertext without padding.\nfunc AESCTRStream(key, iv []byte) (cipher.Stream, error) {\n\tif key == nil || iv == nil {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput\n\t}\n\n\tblockCipher, err := aes.NewCipher(key)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tstream := cipher.NewCTR(blockCipher, iv)\n\n\treturn stream, nil\n}\n\n// DeriveKey takes a passphrase of any length and returns a key of fixed size.\n//\n// If no salt is provided, a new one will be created and returned.\nfunc DeriveKey(passphrase, salt []byte) ([]byte, []byte, error) {\n\tif salt == nil {\n\t\tvar err error\n\t\tsalt, err = GenerateNonceSize(ScryptKeyLen)\n\t\tif err != nil {\n\t\t\treturn nil, nil, err\n\t\t}\n\t}\n\n\tkey, err := scrypt.Key(passphrase, salt, ScryptIterations, ScryptR, ScryptP, ScryptKeyLen)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\treturn key, salt, nil\n}\n\n// PublicKeyToCurve25519 converts an Ed25519 public key into the curve25519\n// public key that would be generated from the same private key.\nfunc PublicKeyToCurve25519(ret *[32]byte, publicKey ed25519.PublicKey) error {\n\tpoint, err := edwards25519.NewGeneratorPoint().SetBytes(publicKey)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to generate point from publicKey: %w\", err)\n\t}\n\n\tcopy(ret[:], point.BytesMontgomery())\n\treturn nil\n}\n\n// from: https://github.com/agl/ed25519/blob/5312a61534124124185d41f09206b9fef1d88403/extra25519/extra25519.go#LL16C11-L16C11\n// PrivateKeyToCurve25519 converts an ed25519 private key into a corresponding\n// curve25519 private key such that the resulting curve25519 public key will\n// equal the result from PublicKeyToCurve25519.\nfunc PrivateKeyToCurve25519(ret *[32]byte, privateKey ed25519.PrivateKey) {\n\th := sha512.New()\n\th.Write(privateKey.Seed())\n\tcopy(ret[:], h.Sum(nil))\n\n\tret[0] &= 248\n\tret[31] &= 127\n\tret[31] |= 64\n}\n"
  },
  {
    "path": "pkg/cryptoutil/cryptoutil_test.go",
    "content": "package cryptoutil\n\nimport (\n\t\"bytes\"\n\t\"crypto/aes\"\n\t\"crypto/rand\"\n\t\"testing\"\n\n\t\"github.com/libp2p/go-libp2p/core/crypto\"\n\t\"github.com/stretchr/testify/require\"\n\t\"golang.org/x/crypto/curve25519\"\n\t\"golang.org/x/crypto/ed25519\"\n\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n)\n\n// from: https://github.com/agl/ed25519/blob/5312a61534124124185d41f09206b9fef1d88403/extra25519/extra25519_test.go#L16-L30\nfunc TestCurve25519Conversion(t *testing.T) {\n\tpublic, private, err := ed25519.GenerateKey(rand.Reader)\n\trequire.NoError(t, err)\n\n\tvar curve25519Public, curve25519Public2, curve25519Private [32]byte\n\tPrivateKeyToCurve25519(&curve25519Private, private)\n\tcurve25519.ScalarBaseMult(&curve25519Public, &curve25519Private)\n\n\terr = PublicKeyToCurve25519(&curve25519Public2, public)\n\trequire.NoError(t, err)\n\n\trequire.Truef(t, bytes.Equal(curve25519Public[:], curve25519Public2[:]),\n\t\t\"Values didn't match: curve25519 produced %x, conversion produced %x\", curve25519Public[:], curve25519Public2[:])\n}\n\nfunc TestSeedFromEd25519PrivateKey(t *testing.T) {\n\tpriv, _, _ := crypto.GenerateECDSAKeyPair(rand.Reader)\n\t_, err := SeedFromEd25519PrivateKey(priv)\n\tif err != errcode.ErrCode_ErrInvalidInput {\n\t\tt.Error(\"Should fail with ErrInvalidInput\")\n\t}\n\tpriv, _, _ = crypto.GenerateEd25519Key(rand.Reader)\n\t_, err = SeedFromEd25519PrivateKey(priv)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n}\n\nfunc TestEdwardsToMontgomeryPub(t *testing.T) {\n\t_, pub, _ := crypto.GenerateEd25519Key(rand.Reader)\n\t_, err := EdwardsToMontgomeryPub(pub)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\t_, pub, _ = crypto.GenerateECDSAKeyPair(rand.Reader)\n\t_, err = EdwardsToMontgomeryPub(pub)\n\tif err != errcode.ErrCode_ErrInvalidInput {\n\t\tt.Error(\"Should fail with ErrInvalidInput\")\n\t}\n}\n\nfunc TestEdwardsToMontgomeryPriv(t *testing.T) {\n\tpriv, _, _ := crypto.GenerateEd25519Key(rand.Reader)\n\t_, err := EdwardsToMontgomeryPriv(priv)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tpriv, _, _ = crypto.GenerateECDSAKeyPair(rand.Reader)\n\t_, err = EdwardsToMontgomeryPriv(priv)\n\tif err != errcode.ErrCode_ErrInvalidInput {\n\t\tt.Error(\"Should fail with ErrInvalidInput\")\n\t}\n}\n\nfunc TestDeriveKey(t *testing.T) {\n\tcases := []struct {\n\t\tpassphrase []byte\n\t\tsalt       []byte\n\t}{\n\t\t{nil, nil},\n\t\t{[]byte{0x42}, []byte{0x42}},\n\t\t{nil, []byte{0x42}},\n\t\t{[]byte{0x42}, nil},\n\t\t{[]byte(\"hello world\"), []byte(\"hello world\")},\n\t\t{[]byte(\"morethan32bytes..................\"), []byte(\"hello world\")},\n\t\t{[]byte(\"hello world\"), []byte(\"morethan32bytes..................\")},\n\t\t{[]byte(\"morethan32bytes..................\"), []byte(\"morethan32bytes..................\")},\n\t}\n\tfor _, tc := range cases {\n\t\tt.Run(\"\", func(t *testing.T) {\n\t\t\tkey, salt, err := DeriveKey(tc.passphrase, tc.salt)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Equal(t, ScryptKeyLen, len(key))\n\t\t\tif tc.salt != nil {\n\t\t\t\trequire.Equal(t, tc.salt, salt)\n\t\t\t}\n\t\t\trequire.NotEqual(t, tc.passphrase, key)\n\t\t})\n\t}\n}\n\nfunc TestAESGCMEncryptDecrypt(t *testing.T) {\n\tvar (\n\t\tpassphrase = []byte(\"my priv4te k3y\")\n\t\tsalt       = []byte(\"my sup3r s3lt\")\n\t\tmessage    = []byte(\"12345678901234567890123456789012\")\n\t)\n\tkey, salt, err := DeriveKey(passphrase, salt)\n\trequire.NoError(t, err)\n\trequire.Equal(t, salt, salt)\n\trequire.NotEqual(t, passphrase, key)\n\trequire.Equal(t, ScryptKeyLen, len(key))\n\n\tenc, err := AESGCMEncrypt(key, message)\n\trequire.NoError(t, err)\n\trequire.NotEqual(t, message, enc)\n\n\tdec, err := AESGCMDecrypt(key, enc)\n\trequire.NoError(t, err)\n\trequire.Equal(t, message, dec)\n}\n\nfunc TestAESCTRStream(t *testing.T) {\n\tvar (\n\t\tpassphrase = []byte(\"my priv4te k3y\")\n\t\tmessage1   = []byte(\"hello world!\")\n\t\tmessage2   = []byte(\"hi planet!\")\n\t\tenc1       = make([]byte, len(message1))\n\t\tenc2       = make([]byte, len(message2))\n\t)\n\n\t// generate nonce with AES' blocksize\n\tnonce, err := GenerateNonceSize(aes.BlockSize)\n\trequire.NoError(t, err)\n\trequire.NotNil(t, nonce)\n\n\t// derive key for AES\n\tkey, salt, err := DeriveKey(passphrase, nonce)\n\trequire.NoError(t, err)\n\trequire.NotNil(t, key)\n\trequire.Equal(t, nonce, salt)\n\n\t// encrypt\n\t{\n\t\tstream, err := AESCTRStream(key, nonce)\n\t\trequire.NoError(t, err)\n\t\tstream.XORKeyStream(enc1, message1)\n\t\tstream.XORKeyStream(enc2, message2)\n\t\trequire.NotEqual(t, enc1, message1)\n\t\trequire.NotEqual(t, enc2, message2)\n\t\trequire.Equal(t, len(enc1), len(message1))\n\t\trequire.Equal(t, len(enc2), len(message2))\n\t}\n\n\t// decrypt\n\t{\n\t\tvar (\n\t\t\tdec1 = make([]byte, len(message1))\n\t\t\tdec2 = make([]byte, len(message2))\n\t\t)\n\t\tstream, err := AESCTRStream(key, nonce)\n\t\trequire.NoError(t, err)\n\t\tstream.XORKeyStream(dec1, enc1)\n\t\tstream.XORKeyStream(dec2, enc2)\n\t\trequire.Equal(t, dec1, message1)\n\t\trequire.Equal(t, dec2, message2)\n\t}\n\n\t// silently failing invalid decrypt\n\t{\n\t\tinvalidKey, _, err := DeriveKey([]byte(\"invalid passphrase\"), nonce)\n\t\tvar (\n\t\t\tdec1 = make([]byte, len(message1))\n\t\t\tdec2 = make([]byte, len(message2))\n\t\t)\n\t\trequire.NoError(t, err)\n\t\trequire.NotNil(t, invalidKey)\n\t\tstream, err := AESCTRStream(invalidKey, nonce)\n\t\trequire.NoError(t, err)\n\t\tstream.XORKeyStream(dec1, enc1)\n\t\tstream.XORKeyStream(dec2, enc2)\n\t\trequire.NotEqual(t, dec1, message1)\n\t\trequire.NotEqual(t, dec2, message2)\n\t\trequire.NotEqual(t, dec1, enc1)\n\t\trequire.NotEqual(t, dec2, enc2)\n\t\trequire.Equal(t, len(dec1), len(enc1))\n\t\trequire.Equal(t, len(dec2), len(enc2))\n\t}\n}\n"
  },
  {
    "path": "pkg/cryptoutil/doc.go",
    "content": "// Package cryptoutil contains generic & stateless crypto helpers.\npackage cryptoutil\n"
  },
  {
    "path": "pkg/cryptoutil/signer_wrapper.go",
    "content": "package cryptoutil\n\nimport (\n\tstdcrypto \"crypto\"\n\t\"io\"\n\n\tlibp2p_ci \"github.com/libp2p/go-libp2p/core/crypto\"\n)\n\nfunc NewFuncSigner(key libp2p_ci.PubKey, signer func([]byte) ([]byte, error)) stdcrypto.Signer {\n\treturn &funcSigner{\n\t\tpubKey: key,\n\t\tsigner: signer,\n\t}\n}\n\ntype funcSigner struct {\n\tpubKey libp2p_ci.PubKey\n\tsigner func([]byte) ([]byte, error)\n}\n\nfunc (f *funcSigner) Public() stdcrypto.PublicKey {\n\treturn f.pubKey\n}\n\nfunc (f *funcSigner) Sign(_ io.Reader, digest []byte, _ stdcrypto.SignerOpts) (signature []byte, err error) {\n\treturn f.signer(digest)\n}\n"
  },
  {
    "path": "pkg/errcode/doc.go",
    "content": "// Package errcode contains the list of Berty error codes.\npackage errcode\n"
  },
  {
    "path": "pkg/errcode/errcode.pb.go",
    "content": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.34.2\n// \tprotoc        (unknown)\n// source: errcode/errcode.proto\n\npackage errcode\n\nimport (\n\tprotoreflect \"google.golang.org/protobuf/reflect/protoreflect\"\n\tprotoimpl \"google.golang.org/protobuf/runtime/protoimpl\"\n\treflect \"reflect\"\n\tsync \"sync\"\n)\n\nconst (\n\t// Verify that this generated code is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)\n\t// Verify that runtime/protoimpl is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)\n)\n\ntype ErrCode int32\n\nconst (\n\tErrCode_Undefined                                            ErrCode = 0   // default value, should never be set manually\n\tErrCode_TODO                                                 ErrCode = 666 // indicates that you plan to create an error later\n\tErrCode_ErrNotImplemented                                    ErrCode = 777 // indicates that a method is not implemented yet\n\tErrCode_ErrInternal                                          ErrCode = 888 // indicates an unknown error (without Code), i.e. in gRPC\n\tErrCode_ErrInvalidInput                                      ErrCode = 100\n\tErrCode_ErrInvalidRange                                      ErrCode = 101\n\tErrCode_ErrMissingInput                                      ErrCode = 102\n\tErrCode_ErrSerialization                                     ErrCode = 103\n\tErrCode_ErrDeserialization                                   ErrCode = 104\n\tErrCode_ErrStreamRead                                        ErrCode = 105\n\tErrCode_ErrStreamWrite                                       ErrCode = 106\n\tErrCode_ErrStreamTransform                                   ErrCode = 110\n\tErrCode_ErrStreamSendAndClose                                ErrCode = 111\n\tErrCode_ErrStreamHeaderWrite                                 ErrCode = 112\n\tErrCode_ErrStreamHeaderRead                                  ErrCode = 115\n\tErrCode_ErrStreamSink                                        ErrCode = 113\n\tErrCode_ErrStreamCloseAndRecv                                ErrCode = 114\n\tErrCode_ErrMissingMapKey                                     ErrCode = 107\n\tErrCode_ErrDBWrite                                           ErrCode = 108\n\tErrCode_ErrDBRead                                            ErrCode = 109\n\tErrCode_ErrDBDestroy                                         ErrCode = 120\n\tErrCode_ErrDBMigrate                                         ErrCode = 121\n\tErrCode_ErrDBReplay                                          ErrCode = 122\n\tErrCode_ErrDBRestore                                         ErrCode = 123\n\tErrCode_ErrDBOpen                                            ErrCode = 124\n\tErrCode_ErrDBClose                                           ErrCode = 125\n\tErrCode_ErrCryptoRandomGeneration                            ErrCode = 200\n\tErrCode_ErrCryptoKeyGeneration                               ErrCode = 201\n\tErrCode_ErrCryptoNonceGeneration                             ErrCode = 202\n\tErrCode_ErrCryptoSignature                                   ErrCode = 203\n\tErrCode_ErrCryptoSignatureVerification                       ErrCode = 204\n\tErrCode_ErrCryptoDecrypt                                     ErrCode = 205\n\tErrCode_ErrCryptoDecryptPayload                              ErrCode = 206\n\tErrCode_ErrCryptoEncrypt                                     ErrCode = 207\n\tErrCode_ErrCryptoKeyConversion                               ErrCode = 208\n\tErrCode_ErrCryptoCipherInit                                  ErrCode = 209\n\tErrCode_ErrCryptoKeyDerivation                               ErrCode = 210\n\tErrCode_ErrMap                                               ErrCode = 300\n\tErrCode_ErrForEach                                           ErrCode = 301\n\tErrCode_ErrKeystoreGet                                       ErrCode = 400\n\tErrCode_ErrKeystorePut                                       ErrCode = 401\n\tErrCode_ErrNotFound                                          ErrCode = 404 // generic\n\tErrCode_ErrOrbitDBInit                                       ErrCode = 1000\n\tErrCode_ErrOrbitDBOpen                                       ErrCode = 1001\n\tErrCode_ErrOrbitDBAppend                                     ErrCode = 1002\n\tErrCode_ErrOrbitDBDeserialization                            ErrCode = 1003\n\tErrCode_ErrOrbitDBStoreCast                                  ErrCode = 1004\n\tErrCode_ErrHandshakeOwnEphemeralKeyGenSend                   ErrCode = 1100\n\tErrCode_ErrHandshakePeerEphemeralKeyRecv                     ErrCode = 1101\n\tErrCode_ErrHandshakeRequesterAuthenticateBoxKeyGen           ErrCode = 1102\n\tErrCode_ErrHandshakeResponderAcceptBoxKeyGen                 ErrCode = 1103\n\tErrCode_ErrHandshakeRequesterHello                           ErrCode = 1104\n\tErrCode_ErrHandshakeResponderHello                           ErrCode = 1105\n\tErrCode_ErrHandshakeRequesterAuthenticate                    ErrCode = 1106\n\tErrCode_ErrHandshakeResponderAccept                          ErrCode = 1107\n\tErrCode_ErrHandshakeRequesterAcknowledge                     ErrCode = 1108\n\tErrCode_ErrContactRequestSameAccount                         ErrCode = 1200\n\tErrCode_ErrContactRequestContactAlreadyAdded                 ErrCode = 1201\n\tErrCode_ErrContactRequestContactBlocked                      ErrCode = 1202\n\tErrCode_ErrContactRequestContactUndefined                    ErrCode = 1203\n\tErrCode_ErrContactRequestIncomingAlreadyReceived             ErrCode = 1204\n\tErrCode_ErrGroupMemberLogEventOpen                           ErrCode = 1300\n\tErrCode_ErrGroupMemberLogEventSignature                      ErrCode = 1301\n\tErrCode_ErrGroupMemberUnknownGroupID                         ErrCode = 1302\n\tErrCode_ErrGroupSecretOtherDestMember                        ErrCode = 1303\n\tErrCode_ErrGroupSecretAlreadySentToMember                    ErrCode = 1304\n\tErrCode_ErrGroupInvalidType                                  ErrCode = 1305\n\tErrCode_ErrGroupMissing                                      ErrCode = 1306\n\tErrCode_ErrGroupActivate                                     ErrCode = 1307\n\tErrCode_ErrGroupDeactivate                                   ErrCode = 1308\n\tErrCode_ErrGroupInfo                                         ErrCode = 1309\n\tErrCode_ErrGroupUnknown                                      ErrCode = 1310\n\tErrCode_ErrGroupOpen                                         ErrCode = 1311\n\tErrCode_ErrMessageKeyPersistencePut                          ErrCode = 1500\n\tErrCode_ErrMessageKeyPersistenceGet                          ErrCode = 1501\n\tErrCode_ErrServiceReplication                                ErrCode = 4100\n\tErrCode_ErrServiceReplicationServer                          ErrCode = 4101\n\tErrCode_ErrServiceReplicationMissingEndpoint                 ErrCode = 4102\n\tErrCode_ErrServicesDirectory                                 ErrCode = 4200\n\tErrCode_ErrServicesDirectoryInvalidVerifiedCredentialSubject ErrCode = 4201\n\tErrCode_ErrServicesDirectoryExistingRecordNotFound           ErrCode = 4202\n\tErrCode_ErrServicesDirectoryRecordLockedAndCantBeReplaced    ErrCode = 4203\n\tErrCode_ErrServicesDirectoryExplicitReplaceFlagRequired      ErrCode = 4204\n\tErrCode_ErrServicesDirectoryInvalidVerifiedCredential        ErrCode = 4205\n\tErrCode_ErrServicesDirectoryExpiredVerifiedCredential        ErrCode = 4206\n\tErrCode_ErrServicesDirectoryInvalidVerifiedCredentialID      ErrCode = 4207\n)\n\n// Enum value maps for ErrCode.\nvar (\n\tErrCode_name = map[int32]string{\n\t\t0:    \"Undefined\",\n\t\t666:  \"TODO\",\n\t\t777:  \"ErrNotImplemented\",\n\t\t888:  \"ErrInternal\",\n\t\t100:  \"ErrInvalidInput\",\n\t\t101:  \"ErrInvalidRange\",\n\t\t102:  \"ErrMissingInput\",\n\t\t103:  \"ErrSerialization\",\n\t\t104:  \"ErrDeserialization\",\n\t\t105:  \"ErrStreamRead\",\n\t\t106:  \"ErrStreamWrite\",\n\t\t110:  \"ErrStreamTransform\",\n\t\t111:  \"ErrStreamSendAndClose\",\n\t\t112:  \"ErrStreamHeaderWrite\",\n\t\t115:  \"ErrStreamHeaderRead\",\n\t\t113:  \"ErrStreamSink\",\n\t\t114:  \"ErrStreamCloseAndRecv\",\n\t\t107:  \"ErrMissingMapKey\",\n\t\t108:  \"ErrDBWrite\",\n\t\t109:  \"ErrDBRead\",\n\t\t120:  \"ErrDBDestroy\",\n\t\t121:  \"ErrDBMigrate\",\n\t\t122:  \"ErrDBReplay\",\n\t\t123:  \"ErrDBRestore\",\n\t\t124:  \"ErrDBOpen\",\n\t\t125:  \"ErrDBClose\",\n\t\t200:  \"ErrCryptoRandomGeneration\",\n\t\t201:  \"ErrCryptoKeyGeneration\",\n\t\t202:  \"ErrCryptoNonceGeneration\",\n\t\t203:  \"ErrCryptoSignature\",\n\t\t204:  \"ErrCryptoSignatureVerification\",\n\t\t205:  \"ErrCryptoDecrypt\",\n\t\t206:  \"ErrCryptoDecryptPayload\",\n\t\t207:  \"ErrCryptoEncrypt\",\n\t\t208:  \"ErrCryptoKeyConversion\",\n\t\t209:  \"ErrCryptoCipherInit\",\n\t\t210:  \"ErrCryptoKeyDerivation\",\n\t\t300:  \"ErrMap\",\n\t\t301:  \"ErrForEach\",\n\t\t400:  \"ErrKeystoreGet\",\n\t\t401:  \"ErrKeystorePut\",\n\t\t404:  \"ErrNotFound\",\n\t\t1000: \"ErrOrbitDBInit\",\n\t\t1001: \"ErrOrbitDBOpen\",\n\t\t1002: \"ErrOrbitDBAppend\",\n\t\t1003: \"ErrOrbitDBDeserialization\",\n\t\t1004: \"ErrOrbitDBStoreCast\",\n\t\t1100: \"ErrHandshakeOwnEphemeralKeyGenSend\",\n\t\t1101: \"ErrHandshakePeerEphemeralKeyRecv\",\n\t\t1102: \"ErrHandshakeRequesterAuthenticateBoxKeyGen\",\n\t\t1103: \"ErrHandshakeResponderAcceptBoxKeyGen\",\n\t\t1104: \"ErrHandshakeRequesterHello\",\n\t\t1105: \"ErrHandshakeResponderHello\",\n\t\t1106: \"ErrHandshakeRequesterAuthenticate\",\n\t\t1107: \"ErrHandshakeResponderAccept\",\n\t\t1108: \"ErrHandshakeRequesterAcknowledge\",\n\t\t1200: \"ErrContactRequestSameAccount\",\n\t\t1201: \"ErrContactRequestContactAlreadyAdded\",\n\t\t1202: \"ErrContactRequestContactBlocked\",\n\t\t1203: \"ErrContactRequestContactUndefined\",\n\t\t1204: \"ErrContactRequestIncomingAlreadyReceived\",\n\t\t1300: \"ErrGroupMemberLogEventOpen\",\n\t\t1301: \"ErrGroupMemberLogEventSignature\",\n\t\t1302: \"ErrGroupMemberUnknownGroupID\",\n\t\t1303: \"ErrGroupSecretOtherDestMember\",\n\t\t1304: \"ErrGroupSecretAlreadySentToMember\",\n\t\t1305: \"ErrGroupInvalidType\",\n\t\t1306: \"ErrGroupMissing\",\n\t\t1307: \"ErrGroupActivate\",\n\t\t1308: \"ErrGroupDeactivate\",\n\t\t1309: \"ErrGroupInfo\",\n\t\t1310: \"ErrGroupUnknown\",\n\t\t1311: \"ErrGroupOpen\",\n\t\t1500: \"ErrMessageKeyPersistencePut\",\n\t\t1501: \"ErrMessageKeyPersistenceGet\",\n\t\t4100: \"ErrServiceReplication\",\n\t\t4101: \"ErrServiceReplicationServer\",\n\t\t4102: \"ErrServiceReplicationMissingEndpoint\",\n\t\t4200: \"ErrServicesDirectory\",\n\t\t4201: \"ErrServicesDirectoryInvalidVerifiedCredentialSubject\",\n\t\t4202: \"ErrServicesDirectoryExistingRecordNotFound\",\n\t\t4203: \"ErrServicesDirectoryRecordLockedAndCantBeReplaced\",\n\t\t4204: \"ErrServicesDirectoryExplicitReplaceFlagRequired\",\n\t\t4205: \"ErrServicesDirectoryInvalidVerifiedCredential\",\n\t\t4206: \"ErrServicesDirectoryExpiredVerifiedCredential\",\n\t\t4207: \"ErrServicesDirectoryInvalidVerifiedCredentialID\",\n\t}\n\tErrCode_value = map[string]int32{\n\t\t\"Undefined\":                          0,\n\t\t\"TODO\":                               666,\n\t\t\"ErrNotImplemented\":                  777,\n\t\t\"ErrInternal\":                        888,\n\t\t\"ErrInvalidInput\":                    100,\n\t\t\"ErrInvalidRange\":                    101,\n\t\t\"ErrMissingInput\":                    102,\n\t\t\"ErrSerialization\":                   103,\n\t\t\"ErrDeserialization\":                 104,\n\t\t\"ErrStreamRead\":                      105,\n\t\t\"ErrStreamWrite\":                     106,\n\t\t\"ErrStreamTransform\":                 110,\n\t\t\"ErrStreamSendAndClose\":              111,\n\t\t\"ErrStreamHeaderWrite\":               112,\n\t\t\"ErrStreamHeaderRead\":                115,\n\t\t\"ErrStreamSink\":                      113,\n\t\t\"ErrStreamCloseAndRecv\":              114,\n\t\t\"ErrMissingMapKey\":                   107,\n\t\t\"ErrDBWrite\":                         108,\n\t\t\"ErrDBRead\":                          109,\n\t\t\"ErrDBDestroy\":                       120,\n\t\t\"ErrDBMigrate\":                       121,\n\t\t\"ErrDBReplay\":                        122,\n\t\t\"ErrDBRestore\":                       123,\n\t\t\"ErrDBOpen\":                          124,\n\t\t\"ErrDBClose\":                         125,\n\t\t\"ErrCryptoRandomGeneration\":          200,\n\t\t\"ErrCryptoKeyGeneration\":             201,\n\t\t\"ErrCryptoNonceGeneration\":           202,\n\t\t\"ErrCryptoSignature\":                 203,\n\t\t\"ErrCryptoSignatureVerification\":     204,\n\t\t\"ErrCryptoDecrypt\":                   205,\n\t\t\"ErrCryptoDecryptPayload\":            206,\n\t\t\"ErrCryptoEncrypt\":                   207,\n\t\t\"ErrCryptoKeyConversion\":             208,\n\t\t\"ErrCryptoCipherInit\":                209,\n\t\t\"ErrCryptoKeyDerivation\":             210,\n\t\t\"ErrMap\":                             300,\n\t\t\"ErrForEach\":                         301,\n\t\t\"ErrKeystoreGet\":                     400,\n\t\t\"ErrKeystorePut\":                     401,\n\t\t\"ErrNotFound\":                        404,\n\t\t\"ErrOrbitDBInit\":                     1000,\n\t\t\"ErrOrbitDBOpen\":                     1001,\n\t\t\"ErrOrbitDBAppend\":                   1002,\n\t\t\"ErrOrbitDBDeserialization\":          1003,\n\t\t\"ErrOrbitDBStoreCast\":                1004,\n\t\t\"ErrHandshakeOwnEphemeralKeyGenSend\": 1100,\n\t\t\"ErrHandshakePeerEphemeralKeyRecv\":   1101,\n\t\t\"ErrHandshakeRequesterAuthenticateBoxKeyGen\":           1102,\n\t\t\"ErrHandshakeResponderAcceptBoxKeyGen\":                 1103,\n\t\t\"ErrHandshakeRequesterHello\":                           1104,\n\t\t\"ErrHandshakeResponderHello\":                           1105,\n\t\t\"ErrHandshakeRequesterAuthenticate\":                    1106,\n\t\t\"ErrHandshakeResponderAccept\":                          1107,\n\t\t\"ErrHandshakeRequesterAcknowledge\":                     1108,\n\t\t\"ErrContactRequestSameAccount\":                         1200,\n\t\t\"ErrContactRequestContactAlreadyAdded\":                 1201,\n\t\t\"ErrContactRequestContactBlocked\":                      1202,\n\t\t\"ErrContactRequestContactUndefined\":                    1203,\n\t\t\"ErrContactRequestIncomingAlreadyReceived\":             1204,\n\t\t\"ErrGroupMemberLogEventOpen\":                           1300,\n\t\t\"ErrGroupMemberLogEventSignature\":                      1301,\n\t\t\"ErrGroupMemberUnknownGroupID\":                         1302,\n\t\t\"ErrGroupSecretOtherDestMember\":                        1303,\n\t\t\"ErrGroupSecretAlreadySentToMember\":                    1304,\n\t\t\"ErrGroupInvalidType\":                                  1305,\n\t\t\"ErrGroupMissing\":                                      1306,\n\t\t\"ErrGroupActivate\":                                     1307,\n\t\t\"ErrGroupDeactivate\":                                   1308,\n\t\t\"ErrGroupInfo\":                                         1309,\n\t\t\"ErrGroupUnknown\":                                      1310,\n\t\t\"ErrGroupOpen\":                                         1311,\n\t\t\"ErrMessageKeyPersistencePut\":                          1500,\n\t\t\"ErrMessageKeyPersistenceGet\":                          1501,\n\t\t\"ErrServiceReplication\":                                4100,\n\t\t\"ErrServiceReplicationServer\":                          4101,\n\t\t\"ErrServiceReplicationMissingEndpoint\":                 4102,\n\t\t\"ErrServicesDirectory\":                                 4200,\n\t\t\"ErrServicesDirectoryInvalidVerifiedCredentialSubject\": 4201,\n\t\t\"ErrServicesDirectoryExistingRecordNotFound\":           4202,\n\t\t\"ErrServicesDirectoryRecordLockedAndCantBeReplaced\":    4203,\n\t\t\"ErrServicesDirectoryExplicitReplaceFlagRequired\":      4204,\n\t\t\"ErrServicesDirectoryInvalidVerifiedCredential\":        4205,\n\t\t\"ErrServicesDirectoryExpiredVerifiedCredential\":        4206,\n\t\t\"ErrServicesDirectoryInvalidVerifiedCredentialID\":      4207,\n\t}\n)\n\nfunc (x ErrCode) Enum() *ErrCode {\n\tp := new(ErrCode)\n\t*p = x\n\treturn p\n}\n\nfunc (x ErrCode) String() string {\n\treturn protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))\n}\n\nfunc (ErrCode) Descriptor() protoreflect.EnumDescriptor {\n\treturn file_errcode_errcode_proto_enumTypes[0].Descriptor()\n}\n\nfunc (ErrCode) Type() protoreflect.EnumType {\n\treturn &file_errcode_errcode_proto_enumTypes[0]\n}\n\nfunc (x ErrCode) Number() protoreflect.EnumNumber {\n\treturn protoreflect.EnumNumber(x)\n}\n\n// Deprecated: Use ErrCode.Descriptor instead.\nfunc (ErrCode) EnumDescriptor() ([]byte, []int) {\n\treturn file_errcode_errcode_proto_rawDescGZIP(), []int{0}\n}\n\ntype ErrDetails struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tCodes []ErrCode `protobuf:\"varint,1,rep,packed,name=codes,proto3,enum=weshnet.errcode.ErrCode\" json:\"codes,omitempty\"`\n}\n\nfunc (x *ErrDetails) Reset() {\n\t*x = ErrDetails{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_errcode_errcode_proto_msgTypes[0]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ErrDetails) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ErrDetails) ProtoMessage() {}\n\nfunc (x *ErrDetails) ProtoReflect() protoreflect.Message {\n\tmi := &file_errcode_errcode_proto_msgTypes[0]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ErrDetails.ProtoReflect.Descriptor instead.\nfunc (*ErrDetails) Descriptor() ([]byte, []int) {\n\treturn file_errcode_errcode_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *ErrDetails) GetCodes() []ErrCode {\n\tif x != nil {\n\t\treturn x.Codes\n\t}\n\treturn nil\n}\n\nvar File_errcode_errcode_proto protoreflect.FileDescriptor\n\nvar file_errcode_errcode_proto_rawDesc = []byte{\n\t0x0a, 0x15, 0x65, 0x72, 0x72, 0x63, 0x6f, 0x64, 0x65, 0x2f, 0x65, 0x72, 0x72, 0x63, 0x6f, 0x64,\n\t0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74,\n\t0x2e, 0x65, 0x72, 0x72, 0x63, 0x6f, 0x64, 0x65, 0x22, 0x3c, 0x0a, 0x0a, 0x45, 0x72, 0x72, 0x44,\n\t0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x2e, 0x0a, 0x05, 0x63, 0x6f, 0x64, 0x65, 0x73, 0x18,\n\t0x01, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e,\n\t0x65, 0x72, 0x72, 0x63, 0x6f, 0x64, 0x65, 0x2e, 0x45, 0x72, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x52,\n\t0x05, 0x63, 0x6f, 0x64, 0x65, 0x73, 0x2a, 0xdb, 0x13, 0x0a, 0x07, 0x45, 0x72, 0x72, 0x43, 0x6f,\n\t0x64, 0x65, 0x12, 0x0d, 0x0a, 0x09, 0x55, 0x6e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x10,\n\t0x00, 0x12, 0x09, 0x0a, 0x04, 0x54, 0x4f, 0x44, 0x4f, 0x10, 0x9a, 0x05, 0x12, 0x16, 0x0a, 0x11,\n\t0x45, 0x72, 0x72, 0x4e, 0x6f, 0x74, 0x49, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x65,\n\t0x64, 0x10, 0x89, 0x06, 0x12, 0x10, 0x0a, 0x0b, 0x45, 0x72, 0x72, 0x49, 0x6e, 0x74, 0x65, 0x72,\n\t0x6e, 0x61, 0x6c, 0x10, 0xf8, 0x06, 0x12, 0x13, 0x0a, 0x0f, 0x45, 0x72, 0x72, 0x49, 0x6e, 0x76,\n\t0x61, 0x6c, 0x69, 0x64, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x10, 0x64, 0x12, 0x13, 0x0a, 0x0f, 0x45,\n\t0x72, 0x72, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x10, 0x65,\n\t0x12, 0x13, 0x0a, 0x0f, 0x45, 0x72, 0x72, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x49, 0x6e,\n\t0x70, 0x75, 0x74, 0x10, 0x66, 0x12, 0x14, 0x0a, 0x10, 0x45, 0x72, 0x72, 0x53, 0x65, 0x72, 0x69,\n\t0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x10, 0x67, 0x12, 0x16, 0x0a, 0x12, 0x45,\n\t0x72, 0x72, 0x44, 0x65, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f,\n\t0x6e, 0x10, 0x68, 0x12, 0x11, 0x0a, 0x0d, 0x45, 0x72, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d,\n\t0x52, 0x65, 0x61, 0x64, 0x10, 0x69, 0x12, 0x12, 0x0a, 0x0e, 0x45, 0x72, 0x72, 0x53, 0x74, 0x72,\n\t0x65, 0x61, 0x6d, 0x57, 0x72, 0x69, 0x74, 0x65, 0x10, 0x6a, 0x12, 0x16, 0x0a, 0x12, 0x45, 0x72,\n\t0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d,\n\t0x10, 0x6e, 0x12, 0x19, 0x0a, 0x15, 0x45, 0x72, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53,\n\t0x65, 0x6e, 0x64, 0x41, 0x6e, 0x64, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x10, 0x6f, 0x12, 0x18, 0x0a,\n\t0x14, 0x45, 0x72, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72,\n\t0x57, 0x72, 0x69, 0x74, 0x65, 0x10, 0x70, 0x12, 0x17, 0x0a, 0x13, 0x45, 0x72, 0x72, 0x53, 0x74,\n\t0x72, 0x65, 0x61, 0x6d, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x61, 0x64, 0x10, 0x73,\n\t0x12, 0x11, 0x0a, 0x0d, 0x45, 0x72, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x69, 0x6e,\n\t0x6b, 0x10, 0x71, 0x12, 0x19, 0x0a, 0x15, 0x45, 0x72, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d,\n\t0x43, 0x6c, 0x6f, 0x73, 0x65, 0x41, 0x6e, 0x64, 0x52, 0x65, 0x63, 0x76, 0x10, 0x72, 0x12, 0x14,\n\t0x0a, 0x10, 0x45, 0x72, 0x72, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x4d, 0x61, 0x70, 0x4b,\n\t0x65, 0x79, 0x10, 0x6b, 0x12, 0x0e, 0x0a, 0x0a, 0x45, 0x72, 0x72, 0x44, 0x42, 0x57, 0x72, 0x69,\n\t0x74, 0x65, 0x10, 0x6c, 0x12, 0x0d, 0x0a, 0x09, 0x45, 0x72, 0x72, 0x44, 0x42, 0x52, 0x65, 0x61,\n\t0x64, 0x10, 0x6d, 0x12, 0x10, 0x0a, 0x0c, 0x45, 0x72, 0x72, 0x44, 0x42, 0x44, 0x65, 0x73, 0x74,\n\t0x72, 0x6f, 0x79, 0x10, 0x78, 0x12, 0x10, 0x0a, 0x0c, 0x45, 0x72, 0x72, 0x44, 0x42, 0x4d, 0x69,\n\t0x67, 0x72, 0x61, 0x74, 0x65, 0x10, 0x79, 0x12, 0x0f, 0x0a, 0x0b, 0x45, 0x72, 0x72, 0x44, 0x42,\n\t0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x10, 0x7a, 0x12, 0x10, 0x0a, 0x0c, 0x45, 0x72, 0x72, 0x44,\n\t0x42, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x10, 0x7b, 0x12, 0x0d, 0x0a, 0x09, 0x45, 0x72,\n\t0x72, 0x44, 0x42, 0x4f, 0x70, 0x65, 0x6e, 0x10, 0x7c, 0x12, 0x0e, 0x0a, 0x0a, 0x45, 0x72, 0x72,\n\t0x44, 0x42, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x10, 0x7d, 0x12, 0x1e, 0x0a, 0x19, 0x45, 0x72, 0x72,\n\t0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x52, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x47, 0x65, 0x6e, 0x65,\n\t0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x10, 0xc8, 0x01, 0x12, 0x1b, 0x0a, 0x16, 0x45, 0x72, 0x72,\n\t0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x4b, 0x65, 0x79, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74,\n\t0x69, 0x6f, 0x6e, 0x10, 0xc9, 0x01, 0x12, 0x1d, 0x0a, 0x18, 0x45, 0x72, 0x72, 0x43, 0x72, 0x79,\n\t0x70, 0x74, 0x6f, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69,\n\t0x6f, 0x6e, 0x10, 0xca, 0x01, 0x12, 0x17, 0x0a, 0x12, 0x45, 0x72, 0x72, 0x43, 0x72, 0x79, 0x70,\n\t0x74, 0x6f, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x10, 0xcb, 0x01, 0x12, 0x23,\n\t0x0a, 0x1e, 0x45, 0x72, 0x72, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x53, 0x69, 0x67, 0x6e, 0x61,\n\t0x74, 0x75, 0x72, 0x65, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,\n\t0x10, 0xcc, 0x01, 0x12, 0x15, 0x0a, 0x10, 0x45, 0x72, 0x72, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f,\n\t0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x10, 0xcd, 0x01, 0x12, 0x1c, 0x0a, 0x17, 0x45, 0x72,\n\t0x72, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x50, 0x61,\n\t0x79, 0x6c, 0x6f, 0x61, 0x64, 0x10, 0xce, 0x01, 0x12, 0x15, 0x0a, 0x10, 0x45, 0x72, 0x72, 0x43,\n\t0x72, 0x79, 0x70, 0x74, 0x6f, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x10, 0xcf, 0x01, 0x12,\n\t0x1b, 0x0a, 0x16, 0x45, 0x72, 0x72, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x4b, 0x65, 0x79, 0x43,\n\t0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x10, 0xd0, 0x01, 0x12, 0x18, 0x0a, 0x13,\n\t0x45, 0x72, 0x72, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, 0x49,\n\t0x6e, 0x69, 0x74, 0x10, 0xd1, 0x01, 0x12, 0x1b, 0x0a, 0x16, 0x45, 0x72, 0x72, 0x43, 0x72, 0x79,\n\t0x70, 0x74, 0x6f, 0x4b, 0x65, 0x79, 0x44, 0x65, 0x72, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e,\n\t0x10, 0xd2, 0x01, 0x12, 0x0b, 0x0a, 0x06, 0x45, 0x72, 0x72, 0x4d, 0x61, 0x70, 0x10, 0xac, 0x02,\n\t0x12, 0x0f, 0x0a, 0x0a, 0x45, 0x72, 0x72, 0x46, 0x6f, 0x72, 0x45, 0x61, 0x63, 0x68, 0x10, 0xad,\n\t0x02, 0x12, 0x13, 0x0a, 0x0e, 0x45, 0x72, 0x72, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65,\n\t0x47, 0x65, 0x74, 0x10, 0x90, 0x03, 0x12, 0x13, 0x0a, 0x0e, 0x45, 0x72, 0x72, 0x4b, 0x65, 0x79,\n\t0x73, 0x74, 0x6f, 0x72, 0x65, 0x50, 0x75, 0x74, 0x10, 0x91, 0x03, 0x12, 0x10, 0x0a, 0x0b, 0x45,\n\t0x72, 0x72, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x94, 0x03, 0x12, 0x13, 0x0a,\n\t0x0e, 0x45, 0x72, 0x72, 0x4f, 0x72, 0x62, 0x69, 0x74, 0x44, 0x42, 0x49, 0x6e, 0x69, 0x74, 0x10,\n\t0xe8, 0x07, 0x12, 0x13, 0x0a, 0x0e, 0x45, 0x72, 0x72, 0x4f, 0x72, 0x62, 0x69, 0x74, 0x44, 0x42,\n\t0x4f, 0x70, 0x65, 0x6e, 0x10, 0xe9, 0x07, 0x12, 0x15, 0x0a, 0x10, 0x45, 0x72, 0x72, 0x4f, 0x72,\n\t0x62, 0x69, 0x74, 0x44, 0x42, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x10, 0xea, 0x07, 0x12, 0x1e,\n\t0x0a, 0x19, 0x45, 0x72, 0x72, 0x4f, 0x72, 0x62, 0x69, 0x74, 0x44, 0x42, 0x44, 0x65, 0x73, 0x65,\n\t0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x10, 0xeb, 0x07, 0x12, 0x18,\n\t0x0a, 0x13, 0x45, 0x72, 0x72, 0x4f, 0x72, 0x62, 0x69, 0x74, 0x44, 0x42, 0x53, 0x74, 0x6f, 0x72,\n\t0x65, 0x43, 0x61, 0x73, 0x74, 0x10, 0xec, 0x07, 0x12, 0x27, 0x0a, 0x22, 0x45, 0x72, 0x72, 0x48,\n\t0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x4f, 0x77, 0x6e, 0x45, 0x70, 0x68, 0x65, 0x6d,\n\t0x65, 0x72, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x47, 0x65, 0x6e, 0x53, 0x65, 0x6e, 0x64, 0x10, 0xcc,\n\t0x08, 0x12, 0x25, 0x0a, 0x20, 0x45, 0x72, 0x72, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b,\n\t0x65, 0x50, 0x65, 0x65, 0x72, 0x45, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x4b, 0x65,\n\t0x79, 0x52, 0x65, 0x63, 0x76, 0x10, 0xcd, 0x08, 0x12, 0x2f, 0x0a, 0x2a, 0x45, 0x72, 0x72, 0x48,\n\t0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65,\n\t0x72, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x42, 0x6f, 0x78,\n\t0x4b, 0x65, 0x79, 0x47, 0x65, 0x6e, 0x10, 0xce, 0x08, 0x12, 0x29, 0x0a, 0x24, 0x45, 0x72, 0x72,\n\t0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x64,\n\t0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x42, 0x6f, 0x78, 0x4b, 0x65, 0x79, 0x47, 0x65,\n\t0x6e, 0x10, 0xcf, 0x08, 0x12, 0x1f, 0x0a, 0x1a, 0x45, 0x72, 0x72, 0x48, 0x61, 0x6e, 0x64, 0x73,\n\t0x68, 0x61, 0x6b, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x72, 0x48, 0x65, 0x6c,\n\t0x6c, 0x6f, 0x10, 0xd0, 0x08, 0x12, 0x1f, 0x0a, 0x1a, 0x45, 0x72, 0x72, 0x48, 0x61, 0x6e, 0x64,\n\t0x73, 0x68, 0x61, 0x6b, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x64, 0x65, 0x72, 0x48, 0x65,\n\t0x6c, 0x6c, 0x6f, 0x10, 0xd1, 0x08, 0x12, 0x26, 0x0a, 0x21, 0x45, 0x72, 0x72, 0x48, 0x61, 0x6e,\n\t0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x72, 0x41,\n\t0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x10, 0xd2, 0x08, 0x12, 0x20,\n\t0x0a, 0x1b, 0x45, 0x72, 0x72, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x52, 0x65,\n\t0x73, 0x70, 0x6f, 0x6e, 0x64, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x10, 0xd3, 0x08,\n\t0x12, 0x25, 0x0a, 0x20, 0x45, 0x72, 0x72, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65,\n\t0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x72, 0x41, 0x63, 0x6b, 0x6e, 0x6f, 0x77, 0x6c,\n\t0x65, 0x64, 0x67, 0x65, 0x10, 0xd4, 0x08, 0x12, 0x21, 0x0a, 0x1c, 0x45, 0x72, 0x72, 0x43, 0x6f,\n\t0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x61, 0x6d, 0x65,\n\t0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x10, 0xb0, 0x09, 0x12, 0x29, 0x0a, 0x24, 0x45, 0x72,\n\t0x72, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x43,\n\t0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x41, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x41, 0x64, 0x64,\n\t0x65, 0x64, 0x10, 0xb1, 0x09, 0x12, 0x24, 0x0a, 0x1f, 0x45, 0x72, 0x72, 0x43, 0x6f, 0x6e, 0x74,\n\t0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63,\n\t0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x10, 0xb2, 0x09, 0x12, 0x26, 0x0a, 0x21, 0x45,\n\t0x72, 0x72, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,\n\t0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x55, 0x6e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64,\n\t0x10, 0xb3, 0x09, 0x12, 0x2d, 0x0a, 0x28, 0x45, 0x72, 0x72, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63,\n\t0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67,\n\t0x41, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x10,\n\t0xb4, 0x09, 0x12, 0x1f, 0x0a, 0x1a, 0x45, 0x72, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65,\n\t0x6d, 0x62, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4f, 0x70, 0x65, 0x6e,\n\t0x10, 0x94, 0x0a, 0x12, 0x24, 0x0a, 0x1f, 0x45, 0x72, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d,\n\t0x65, 0x6d, 0x62, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x69, 0x67,\n\t0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x10, 0x95, 0x0a, 0x12, 0x21, 0x0a, 0x1c, 0x45, 0x72, 0x72,\n\t0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x55, 0x6e, 0x6b, 0x6e, 0x6f,\n\t0x77, 0x6e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x44, 0x10, 0x96, 0x0a, 0x12, 0x22, 0x0a, 0x1d,\n\t0x45, 0x72, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x4f, 0x74,\n\t0x68, 0x65, 0x72, 0x44, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x10, 0x97, 0x0a,\n\t0x12, 0x26, 0x0a, 0x21, 0x45, 0x72, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, 0x63, 0x72,\n\t0x65, 0x74, 0x41, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x53, 0x65, 0x6e, 0x74, 0x54, 0x6f, 0x4d,\n\t0x65, 0x6d, 0x62, 0x65, 0x72, 0x10, 0x98, 0x0a, 0x12, 0x18, 0x0a, 0x13, 0x45, 0x72, 0x72, 0x47,\n\t0x72, 0x6f, 0x75, 0x70, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x54, 0x79, 0x70, 0x65, 0x10,\n\t0x99, 0x0a, 0x12, 0x14, 0x0a, 0x0f, 0x45, 0x72, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x69,\n\t0x73, 0x73, 0x69, 0x6e, 0x67, 0x10, 0x9a, 0x0a, 0x12, 0x15, 0x0a, 0x10, 0x45, 0x72, 0x72, 0x47,\n\t0x72, 0x6f, 0x75, 0x70, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x10, 0x9b, 0x0a, 0x12,\n\t0x17, 0x0a, 0x12, 0x45, 0x72, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x44, 0x65, 0x61, 0x63, 0x74,\n\t0x69, 0x76, 0x61, 0x74, 0x65, 0x10, 0x9c, 0x0a, 0x12, 0x11, 0x0a, 0x0c, 0x45, 0x72, 0x72, 0x47,\n\t0x72, 0x6f, 0x75, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x10, 0x9d, 0x0a, 0x12, 0x14, 0x0a, 0x0f, 0x45,\n\t0x72, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x9e,\n\t0x0a, 0x12, 0x11, 0x0a, 0x0c, 0x45, 0x72, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4f, 0x70, 0x65,\n\t0x6e, 0x10, 0x9f, 0x0a, 0x12, 0x20, 0x0a, 0x1b, 0x45, 0x72, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61,\n\t0x67, 0x65, 0x4b, 0x65, 0x79, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x63, 0x65,\n\t0x50, 0x75, 0x74, 0x10, 0xdc, 0x0b, 0x12, 0x20, 0x0a, 0x1b, 0x45, 0x72, 0x72, 0x4d, 0x65, 0x73,\n\t0x73, 0x61, 0x67, 0x65, 0x4b, 0x65, 0x79, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e,\n\t0x63, 0x65, 0x47, 0x65, 0x74, 0x10, 0xdd, 0x0b, 0x12, 0x1a, 0x0a, 0x15, 0x45, 0x72, 0x72, 0x53,\n\t0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,\n\t0x6e, 0x10, 0x84, 0x20, 0x12, 0x20, 0x0a, 0x1b, 0x45, 0x72, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69,\n\t0x63, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72,\n\t0x76, 0x65, 0x72, 0x10, 0x85, 0x20, 0x12, 0x29, 0x0a, 0x24, 0x45, 0x72, 0x72, 0x53, 0x65, 0x72,\n\t0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d,\n\t0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x10, 0x86,\n\t0x20, 0x12, 0x19, 0x0a, 0x14, 0x45, 0x72, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73,\n\t0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x10, 0xe8, 0x20, 0x12, 0x39, 0x0a, 0x34,\n\t0x45, 0x72, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x44, 0x69, 0x72, 0x65, 0x63,\n\t0x74, 0x6f, 0x72, 0x79, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x56, 0x65, 0x72, 0x69, 0x66,\n\t0x69, 0x65, 0x64, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x75, 0x62,\n\t0x6a, 0x65, 0x63, 0x74, 0x10, 0xe9, 0x20, 0x12, 0x2f, 0x0a, 0x2a, 0x45, 0x72, 0x72, 0x53, 0x65,\n\t0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x45,\n\t0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4e, 0x6f, 0x74,\n\t0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0xea, 0x20, 0x12, 0x36, 0x0a, 0x31, 0x45, 0x72, 0x72, 0x53,\n\t0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79,\n\t0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x41, 0x6e, 0x64, 0x43,\n\t0x61, 0x6e, 0x74, 0x42, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x64, 0x10, 0xeb, 0x20,\n\t0x12, 0x34, 0x0a, 0x2f, 0x45, 0x72, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x44,\n\t0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x45, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74,\n\t0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x52, 0x65, 0x71, 0x75, 0x69,\n\t0x72, 0x65, 0x64, 0x10, 0xec, 0x20, 0x12, 0x32, 0x0a, 0x2d, 0x45, 0x72, 0x72, 0x53, 0x65, 0x72,\n\t0x76, 0x69, 0x63, 0x65, 0x73, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x49, 0x6e,\n\t0x76, 0x61, 0x6c, 0x69, 0x64, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x43, 0x72, 0x65,\n\t0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x10, 0xed, 0x20, 0x12, 0x32, 0x0a, 0x2d, 0x45, 0x72,\n\t0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f,\n\t0x72, 0x79, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x64, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65,\n\t0x64, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x10, 0xee, 0x20, 0x12, 0x34,\n\t0x0a, 0x2f, 0x45, 0x72, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x44, 0x69, 0x72,\n\t0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x56, 0x65, 0x72,\n\t0x69, 0x66, 0x69, 0x65, 0x64, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x49,\n\t0x44, 0x10, 0xef, 0x20, 0x42, 0x23, 0x5a, 0x21, 0x62, 0x65, 0x72, 0x74, 0x79, 0x2e, 0x74, 0x65,\n\t0x63, 0x68, 0x2f, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2f, 0x76, 0x32, 0x2f, 0x70, 0x6b,\n\t0x67, 0x2f, 0x65, 0x72, 0x72, 0x63, 0x6f, 0x64, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,\n\t0x33,\n}\n\nvar (\n\tfile_errcode_errcode_proto_rawDescOnce sync.Once\n\tfile_errcode_errcode_proto_rawDescData = file_errcode_errcode_proto_rawDesc\n)\n\nfunc file_errcode_errcode_proto_rawDescGZIP() []byte {\n\tfile_errcode_errcode_proto_rawDescOnce.Do(func() {\n\t\tfile_errcode_errcode_proto_rawDescData = protoimpl.X.CompressGZIP(file_errcode_errcode_proto_rawDescData)\n\t})\n\treturn file_errcode_errcode_proto_rawDescData\n}\n\nvar file_errcode_errcode_proto_enumTypes = make([]protoimpl.EnumInfo, 1)\nvar file_errcode_errcode_proto_msgTypes = make([]protoimpl.MessageInfo, 1)\nvar file_errcode_errcode_proto_goTypes = []any{\n\t(ErrCode)(0),       // 0: weshnet.errcode.ErrCode\n\t(*ErrDetails)(nil), // 1: weshnet.errcode.ErrDetails\n}\nvar file_errcode_errcode_proto_depIdxs = []int32{\n\t0, // 0: weshnet.errcode.ErrDetails.codes:type_name -> weshnet.errcode.ErrCode\n\t1, // [1:1] is the sub-list for method output_type\n\t1, // [1:1] is the sub-list for method input_type\n\t1, // [1:1] is the sub-list for extension type_name\n\t1, // [1:1] is the sub-list for extension extendee\n\t0, // [0:1] is the sub-list for field type_name\n}\n\nfunc init() { file_errcode_errcode_proto_init() }\nfunc file_errcode_errcode_proto_init() {\n\tif File_errcode_errcode_proto != nil {\n\t\treturn\n\t}\n\tif !protoimpl.UnsafeEnabled {\n\t\tfile_errcode_errcode_proto_msgTypes[0].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ErrDetails); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n\ttype x struct{}\n\tout := protoimpl.TypeBuilder{\n\t\tFile: protoimpl.DescBuilder{\n\t\t\tGoPackagePath: reflect.TypeOf(x{}).PkgPath(),\n\t\t\tRawDescriptor: file_errcode_errcode_proto_rawDesc,\n\t\t\tNumEnums:      1,\n\t\t\tNumMessages:   1,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   0,\n\t\t},\n\t\tGoTypes:           file_errcode_errcode_proto_goTypes,\n\t\tDependencyIndexes: file_errcode_errcode_proto_depIdxs,\n\t\tEnumInfos:         file_errcode_errcode_proto_enumTypes,\n\t\tMessageInfos:      file_errcode_errcode_proto_msgTypes,\n\t}.Build()\n\tFile_errcode_errcode_proto = out.File\n\tfile_errcode_errcode_proto_rawDesc = nil\n\tfile_errcode_errcode_proto_goTypes = nil\n\tfile_errcode_errcode_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "pkg/errcode/error.go",
    "content": "package errcode\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"slices\"\n\n\t\"golang.org/x/xerrors\"\n\t\"google.golang.org/grpc/codes\"\n\t\"google.golang.org/grpc/status\"\n)\n\n// WithCode defines an error that can be used by helpers of this package.\ntype WithCode interface {\n\terror\n\tCode() ErrCode\n}\n\n// Codes returns a list of wrapped codes\nfunc Codes(err error) []ErrCode {\n\tif err == nil {\n\t\treturn nil\n\t}\n\n\tcodes := []ErrCode{}\n\n\tif st := getGRPCStatus(err); st != nil {\n\t\treturn codesFromGRPCStatus(st)\n\t}\n\n\tif code := currentCode(err); code != -1 {\n\t\tcodes = []ErrCode{code}\n\t}\n\tif cause := genericCause(err); cause != nil {\n\t\tcauseCodes := Codes(cause)\n\t\tif len(causeCodes) > 0 {\n\t\t\tcodes = append(codes, Codes(cause)...)\n\t\t}\n\t}\n\n\treturn codes\n}\n\n// Has returns true if one of the error is or contains (wraps) an expected errcode\nfunc Has(err error, code WithCode) bool {\n\tcodeCode := code.Code()\n\treturn slices.Contains(Codes(err), codeCode)\n}\n\n// Is returns true if the top-level error (it doesn't unwrap it) is actually an ErrCode of the same value\nfunc Is(err error, code WithCode) bool {\n\treturn currentCode(err) == code.Code()\n}\n\n// currentCode returns the code of the actual error without trying to unwrap it, or -1.\nfunc currentCode(err error) ErrCode {\n\tif err == nil {\n\t\treturn -1\n\t}\n\n\tif typed, ok := err.(WithCode); ok {\n\t\treturn typed.Code()\n\t}\n\n\tif st := getGRPCStatus(err); st != nil {\n\t\tcodes := codesFromGRPCStatus(st)\n\t\tif len(codes) > 0 {\n\t\t\treturn codes[0]\n\t\t}\n\t\treturn -1\n\t}\n\n\treturn -1\n}\n\n// Code walks the passed error and returns the code of the first ErrCode met, or -1.\nfunc Code(err error) ErrCode {\n\tif err == nil {\n\t\treturn -1\n\t}\n\n\tif code := currentCode(err); code != -1 {\n\t\treturn code\n\t}\n\n\tif cause := genericCause(err); cause != nil {\n\t\treturn Code(cause)\n\t}\n\n\treturn -1\n}\n\n// LastCode walks the passed error and returns the code of the latest ErrCode, or -1.\nfunc LastCode(err error) ErrCode {\n\tif err == nil {\n\t\treturn -1\n\t}\n\n\tif cause := genericCause(err); cause != nil {\n\t\tif ret := LastCode(cause); ret != -1 {\n\t\t\treturn ret\n\t\t}\n\t}\n\n\tif st := getGRPCStatus(err); st != nil {\n\t\tcodes := codesFromGRPCStatus(st)\n\t\tif len(codes) > 0 {\n\t\t\treturn codes[len(codes)-1]\n\t\t}\n\t\treturn -1\n\t}\n\n\treturn currentCode(err)\n}\n\nfunc genericCause(err error) error {\n\ttype causer interface{ Cause() error }\n\ttype wrapper interface{ Unwrap() error }\n\n\tif causer, ok := err.(causer); ok {\n\t\treturn causer.Cause()\n\t}\n\n\tif wrapper, ok := err.(wrapper); ok {\n\t\treturn wrapper.Unwrap()\n\t}\n\n\treturn nil\n}\n\n//\n// Error\n//\n\nfunc (e ErrCode) Error() string {\n\tname, ok := ErrCode_name[int32(e)]\n\tif ok {\n\t\treturn fmt.Sprintf(\"%s(#%d)\", name, e)\n\t}\n\treturn fmt.Sprintf(\"UNKNOWN_ERRCODE(#%d)\", e)\n}\n\nfunc (e ErrCode) Code() ErrCode {\n\treturn e\n}\n\nfunc (e ErrCode) Wrap(inner error) WithCode {\n\treturn wrappedError{\n\t\tcode:  e,\n\t\tinner: inner,\n\t\tframe: xerrors.Caller(1),\n\t}\n}\n\nfunc (e ErrCode) GRPCStatus() *status.Status {\n\tcode := grpcCodeFromWithCode(e)\n\tst, _ := status.New(code, e.Error()).WithDetails(\n\t\t&ErrDetails{Codes: Codes(e)},\n\t)\n\treturn st\n}\n\n//\n// ConfigurableError\n//\n\ntype wrappedError struct {\n\tcode  ErrCode\n\tinner error\n\tframe xerrors.Frame\n}\n\nfunc (e wrappedError) Error() string {\n\treturn fmt.Sprintf(\"%s: %v\", e.code, e.inner)\n}\n\nfunc (e wrappedError) Code() ErrCode {\n\treturn e.code\n}\n\n// Cause returns the inner error (github.com/pkg/errors)\nfunc (e wrappedError) Cause() error {\n\treturn e.inner\n}\n\n// Unwrap returns the inner error (go1.13)\nfunc (e wrappedError) Unwrap() error {\n\treturn e.inner\n}\n\nfunc (e wrappedError) GRPCStatus() *status.Status {\n\tcode := grpcCodeFromWithCode(e)\n\tst, _ := status.New(code, e.Error()).WithDetails(\n\t\t&ErrDetails{Codes: Codes(e)},\n\t)\n\treturn st\n}\n\nfunc (e wrappedError) Format(f fmt.State, c rune) {\n\txerrors.FormatError(e, f, c)\n\tif f.Flag('+') {\n\t\t_, _ = io.WriteString(f, \"\\n\")\n\t\tif sub := genericCause(e); sub != nil {\n\t\t\tif typed, ok := sub.(wrappedError); ok {\n\t\t\t\tsub = lightWrappedError{wrappedError: typed}\n\t\t\t}\n\t\t\tformatter, ok := sub.(fmt.Formatter)\n\t\t\tif ok {\n\t\t\t\tformatter.Format(f, c)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc (e wrappedError) FormatError(p xerrors.Printer) error {\n\tp.Print(e.Error())\n\tif p.Detail() {\n\t\te.frame.Format(p)\n\t}\n\treturn nil\n}\n\n//\n// light wrapper (used to make prettier (less verbose) stacks)\n//\n\ntype lightWrappedError struct {\n\twrappedError\n\tdeepness int\n}\n\nfunc (e lightWrappedError) Error() string { return \"\" }\n\nfunc (e lightWrappedError) Format(f fmt.State, c rune) {\n\txerrors.FormatError(e, f, c)\n\tif f.Flag('+') {\n\t\t_, _ = io.WriteString(f, \"\\n\")\n\t\tif sub := genericCause(e); sub != nil {\n\t\t\tif typed, ok := sub.(wrappedError); ok {\n\t\t\t\tsub = lightWrappedError{wrappedError: typed, deepness: e.deepness + 1}\n\t\t\t}\n\t\t\tformatter, ok := sub.(fmt.Formatter)\n\t\t\tif ok {\n\t\t\t\tformatter.Format(f, c)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc (e lightWrappedError) FormatError(p xerrors.Printer) error {\n\tp.Printf(\"#%d\", e.deepness+1)\n\te.frame.Format(p)\n\treturn nil\n}\n\n//\n// gRPC helpers\n//\n\nfunc codesFromGRPCStatus(st *status.Status) []ErrCode {\n\tdetails := st.Details()\n\tfor _, detail := range details {\n\t\tif typed, ok := detail.(*ErrDetails); ok {\n\t\t\treturn typed.Codes\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc grpcCodeFromWithCode(WithCode) codes.Code {\n\t// here, we can do a big switch case if we plan to make accurate gRPC codes\n\t// but we probably don't care\n\treturn codes.Unavailable\n}\n\ntype gRPCStatus interface{ GRPCStatus() *status.Status }\n\nfunc getGRPCStatus(err error) *status.Status {\n\tif _, ok := err.(WithCode); !ok {\n\t\tif typed, ok := err.(gRPCStatus); ok {\n\t\t\treturn typed.GRPCStatus()\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/errcode/error_test.go",
    "content": "package errcode\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/pkg/errors\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"google.golang.org/grpc/codes\"\n\t\"google.golang.org/grpc/status\"\n)\n\nvar (\n\terrStdHello  = fmt.Errorf(\"hello\")\n\terrCodeUndef = ErrCode(65530) // simulate a client receiving an error generated from a more recent API\n)\n\nfunc TestError(t *testing.T) {\n\t// test instance\n\tvar (\n\t\t_ ErrCode  = ErrCode_ErrNotImplemented\n\t\t_ error    = ErrCode_ErrNotImplemented\n\t\t_ WithCode = ErrCode_ErrNotImplemented\n\t)\n\n\t// table-driven tests\n\ttests := []struct {\n\t\tname             string\n\t\tinput            error\n\t\texpectedString   string\n\t\texpectedCause    error\n\t\texpectedCode     ErrCode\n\t\texpectedLastCode ErrCode\n\t\texpectedCodes    []ErrCode\n\t\thas777           bool\n\t\thas888           bool\n\t\tis777            bool\n\t\tis888            bool\n\t}{\n\t\t{\"ErrInternal\", ErrCode_ErrInternal, \"ErrInternal(#888)\", ErrCode_ErrInternal, 888, 888, []ErrCode{888}, false, true, false, true},\n\t\t{\"ErrNotImplemented\", ErrCode_ErrNotImplemented, \"ErrNotImplemented(#777)\", ErrCode_ErrNotImplemented, 777, 777, []ErrCode{777}, true, false, true, false},\n\t\t{\"ErrNotImplemented.Wrap(ErrInternal)\", ErrCode_ErrNotImplemented.Wrap(ErrCode_ErrInternal), \"ErrNotImplemented(#777): ErrInternal(#888)\", ErrCode_ErrInternal, 777, 888, []ErrCode{777, 888}, true, true, true, false},\n\t\t{\"ErrNotImplemented.Wrap(ErrInternal.Wrap(TODO))\", ErrCode_ErrNotImplemented.Wrap(ErrCode_ErrInternal.Wrap(ErrCode_TODO)), \"ErrNotImplemented(#777): ErrInternal(#888): TODO(#666)\", ErrCode_TODO, 777, 666, []ErrCode{777, 888, 666}, true, true, true, false},\n\t\t{\"ErrNotImplemented.Wrap(ErrInternal.Wrap(errStdHello))\", ErrCode_ErrNotImplemented.Wrap(ErrCode_ErrInternal.Wrap(errStdHello)), \"ErrNotImplemented(#777): ErrInternal(#888): hello\", errStdHello, 777, 888, []ErrCode{777, 888}, true, true, true, false},\n\t\t{\"ErrNotImplemented.Wrap(errStdHello)\", ErrCode_ErrNotImplemented.Wrap(errStdHello), \"ErrNotImplemented(#777): hello\", errStdHello, 777, 777, []ErrCode{777}, true, false, true, false},\n\t\t{\"errCodeUndef\", errCodeUndef, \"UNKNOWN_ERRCODE(#65530)\", errCodeUndef, 65530, 65530, []ErrCode{65530}, false, false, false, false},\n\t\t{\"errStdHello\", errStdHello, \"hello\", errStdHello, -1, -1, []ErrCode{}, false, false, false, false},\n\t\t{\"nil\", nil, \"<nil>\", nil, -1, -1, nil, false, false, false, false},\n\t\t{`errors.Wrap(ErrNotImplemented,blah)`, errors.Wrap(ErrCode_ErrNotImplemented, \"blah\"), \"blah: ErrNotImplemented(#777)\", ErrCode_ErrNotImplemented, 777, 777, []ErrCode{777}, true, false, false, false},\n\t\t{`errors.Wrap(ErrNotImplemented.Wrap(ErrInternal),blah)`, errors.Wrap(ErrCode_ErrNotImplemented.Wrap(ErrCode_ErrInternal), \"blah\"), \"blah: ErrNotImplemented(#777): ErrInternal(#888)\", ErrCode_ErrInternal, 777, 888, []ErrCode{777, 888}, true, true, false, false},\n\t}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tassert.Equal(t, test.expectedString, fmt.Sprint(test.input))\n\t\t\tassert.Equal(t, test.expectedCode, Code(test.input))\n\t\t\tassert.Equal(t, test.expectedLastCode, LastCode(test.input))\n\t\t\tassert.Equal(t, test.expectedCause, errors.Cause(test.input))\n\t\t\tassert.Equal(t, test.expectedCodes, Codes(test.input))\n\t\t\tassert.Equal(t, test.has777, Has(test.input, ErrCode_ErrNotImplemented))\n\t\t\tassert.Equal(t, test.has888, Has(test.input, ErrCode_ErrInternal))\n\t\t\tassert.Equal(t, test.is777, Is(test.input, ErrCode_ErrNotImplemented))\n\t\t\tassert.Equal(t, test.is888, Is(test.input, ErrCode_ErrInternal))\n\t\t})\n\t}\n}\n\nfunc TestStatus(t *testing.T) {\n\ttests := []struct {\n\t\tname             string\n\t\tinput            error\n\t\thas777           bool\n\t\thas888           bool\n\t\texpectedGrpcCode codes.Code\n\t\thasGrpcStatus    bool\n\t}{\n\t\t{\"ErrInternal\", ErrCode_ErrInternal, false, true, codes.Unavailable, true},\n\t\t{\"ErrNotImplemented\", ErrCode_ErrNotImplemented, true, false, codes.Unavailable, true},\n\t\t{\"ErrNotImplemented.Wrap(ErrInternal)\", ErrCode_ErrNotImplemented.Wrap(ErrCode_ErrInternal), true, true, codes.Unavailable, true},\n\t\t{\"ErrNotImplemented.Wrap(ErrInternal.Wrap(ErrNotImplemented))\", ErrCode_ErrNotImplemented.Wrap(ErrCode_ErrInternal.Wrap(ErrCode_ErrNotImplemented)), true, true, codes.Unavailable, true},\n\t\t{\"ErrNotImplemented.Wrap(ErrInternal.Wrap(TODO))\", ErrCode_ErrNotImplemented.Wrap(ErrCode_ErrInternal.Wrap(ErrCode_TODO)), true, true, codes.Unavailable, true},\n\t\t{\"ErrNotImplemented.Wrap(ErrInternal.Wrap(errStdHello))\", ErrCode_ErrNotImplemented.Wrap(ErrCode_ErrInternal.Wrap(errStdHello)), true, true, codes.Unavailable, true},\n\t\t{\"ErrNotImplemented.Wrap(errStdHello)\", ErrCode_ErrNotImplemented.Wrap(errStdHello), true, false, codes.Unavailable, true},\n\t\t{\"errCodeUndef\", errCodeUndef, false, false, codes.Unavailable, true},\n\t\t{\"errStdHello\", errStdHello, false, false, codes.Unknown, false},\n\t\t{\"nil\", nil, false, false, codes.OK, true},\n\t\t{`errors.Wrap(ErrNotImplemented,blah)`, errors.Wrap(ErrCode_ErrNotImplemented, \"blah\"), true, false, codes.Unavailable, true},\n\t\t{`errors.Wrap(ErrNotImplemented.Wrap(ErrInternal},blah)`, errors.Wrap(ErrCode_ErrNotImplemented.Wrap(ErrCode_ErrInternal), \"blah\"), true, true, codes.Unavailable, true},\n\t}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tst, ok := status.FromError(test.input)\n\t\t\tassert.Equal(t, test.hasGrpcStatus, ok)\n\t\t\tif test.input != nil {\n\t\t\t\tassert.Error(t, st.Err())\n\t\t\t\tassert.Equal(t, st.Message(), test.input.Error())\n\t\t\t}\n\t\t\tif test.hasGrpcStatus {\n\t\t\t\tstErr := st.Err()\n\t\t\t\tif test.input != nil {\n\t\t\t\t\tassert.NotNil(t, st)\n\t\t\t\t\tassert.Error(t, stErr)\n\t\t\t\t}\n\t\t\t\tassert.Equal(t, st.Code().String(), test.expectedGrpcCode.String())\n\t\t\t\tassert.Equal(t, Code(test.input), Code(stErr))\n\t\t\t\tassert.Equal(t, LastCode(test.input), LastCode(stErr))\n\t\t\t\tassert.Equal(t, LastCode(test.input), LastCode(stErr))\n\t\t\t\tassert.Equal(t, Codes(test.input), Codes(stErr))\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/errcode/stdproto.go",
    "content": "package errcode\n\n// nolint:staticcheck // cannot use the new protobuf API while keeping gogoproto\n\n// nolint:gochecknoinits // cannot avoid using this init func\n// nolint:staticcheck // cannot use the new protobuf API while keeping gogoproto\nfunc init() {\n\t// the goal of this file is to register types on non-gogo proto (required by status.Details)\n\t// proto.RegisterEnum(\"weshnet.errcode.ErrCode\", ErrCode_name, ErrCode_value) // nolint:staticcheck // cannot use the new protobuf API while keeping gogoproto\n\t// proto.RegisterType((*ErrDetails)(nil), \"weshnet.errcode.ErrDetails\")       // nolint:staticcheck // cannot use the new protobuf API while keeping gogoproto\n}\n"
  },
  {
    "path": "pkg/grpcutil/buf_listener.go",
    "content": "package grpcutil\n\nimport (\n\t\"context\"\n\t\"net\"\n\n\t\"google.golang.org/grpc\"\n\t\"google.golang.org/grpc/credentials/insecure\"\n\t\"google.golang.org/grpc/test/bufconn\"\n)\n\ntype BufListener struct {\n\t*bufconn.Listener\n}\n\nfunc NewBufListener(sz int) *BufListener {\n\treturn &BufListener{\n\t\tListener: bufconn.Listen(sz),\n\t}\n}\n\nfunc (bl *BufListener) dialer(context.Context, string) (net.Conn, error) {\n\treturn bl.Dial()\n}\n\nfunc (bl *BufListener) NewClientConn(_ context.Context, opts ...grpc.DialOption) (*grpc.ClientConn, error) {\n\tmendatoryOpts := []grpc.DialOption{\n\t\tgrpc.WithTransportCredentials(insecure.NewCredentials()),\n\t\tgrpc.WithContextDialer(bl.dialer), // set pipe dialer\n\t}\n\n\treturn grpc.NewClient(\"passthrough://buf\", append(opts, mendatoryOpts...)...)\n}\n"
  },
  {
    "path": "pkg/grpcutil/doc.go",
    "content": "// Package grpcutil contains gRPC lazy codecs, messages and a buf-based listener.\npackage grpcutil\n"
  },
  {
    "path": "pkg/grpcutil/simple_auth.go",
    "content": "package grpcutil\n\nimport (\n\t\"context\"\n\n\t\"google.golang.org/grpc/credentials\"\n)\n\nconst headerAuthorize = \"authorization\"\n\nvar _ credentials.PerRPCCredentials = (*unsecureSimpleAuthAccess)(nil)\n\n// unsecureSimpleAuthAccess supplies PerRPCCredentials from a given token.\ntype unsecureSimpleAuthAccess struct {\n\ttoken  string\n\tscheme string\n}\n\n// NewUnsecureSimpleAuthAccess constructs the PerRPCCredentials using a given token.\nfunc NewUnsecureSimpleAuthAccess(scheme, token string) credentials.PerRPCCredentials {\n\treturn &unsecureSimpleAuthAccess{token: token, scheme: scheme}\n}\n\n// nolint:revive\nfunc (sa *unsecureSimpleAuthAccess) GetRequestMetadata(_ context.Context, uri ...string) (map[string]string, error) {\n\treturn map[string]string{\n\t\theaderAuthorize: \"bearer \" + sa.token,\n\t}, nil\n}\n\nfunc (unsecureSimpleAuthAccess) RequireTransportSecurity() bool {\n\treturn false\n}\n"
  },
  {
    "path": "pkg/grpcutil/simple_auth_test.go",
    "content": "package grpcutil\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"testing\"\n\n\tgrpc_middleware \"github.com/grpc-ecosystem/go-grpc-middleware\"\n\tgrpc_auth \"github.com/grpc-ecosystem/go-grpc-middleware/auth\"\n\tgrpc_ctxtags \"github.com/grpc-ecosystem/go-grpc-middleware/tags\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"google.golang.org/grpc\"\n\t\"google.golang.org/grpc/codes\"\n\tpb \"google.golang.org/grpc/examples/helloworld/helloworld\"\n\t\"google.golang.org/grpc/status\"\n)\n\nconst testGoodToken = \"hellobuddy\"\n\nfunc validateToken(token string) bool {\n\treturn token == testGoodToken\n}\n\n// testAuthFunc is used by a middleware to authenticate requests\nfunc testAuthFunc(ctx context.Context) (context.Context, error) {\n\ttoken, err := grpc_auth.AuthFromMD(ctx, \"bearer\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !validateToken(token) {\n\t\treturn nil, status.Errorf(codes.Unauthenticated, \"invalid auth token: %s\", token)\n\t}\n\n\ttags := grpc_ctxtags.Extract(ctx).Set(\"auth.secret\", token)\n\tnewCtx := grpc_ctxtags.SetInContext(ctx, tags)\n\treturn newCtx, nil\n}\n\nfunc SayHelloAuthenticated(ctx context.Context, request *pb.HelloRequest) (*pb.HelloReply, error) {\n\treturn &pb.HelloReply{Message: \"pong authenticated\"}, nil\n}\n\nfunc TestSimpleAuth(t *testing.T) {\n\tcases := []struct {\n\t\tname       string\n\t\ttoken      string\n\t\tassertFunc assert.ErrorAssertionFunc\n\t}{\n\t\t{name: \"Authenticated\", token: testGoodToken, assertFunc: assert.NoError},\n\t\t{name: \"Unauthenticated\", token: \"badtoken\", assertFunc: assert.Error},\n\t}\n\n\tfor _, tc := range cases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tctx, cancel := context.WithCancel(context.Background())\n\t\t\tdefer cancel()\n\n\t\t\tgrpcServer := grpc.NewServer(\n\t\t\t\tgrpc_middleware.WithUnaryServerChain(\n\t\t\t\t\tgrpc_auth.UnaryServerInterceptor(testAuthFunc),\n\t\t\t\t),\n\t\t\t\tgrpc_middleware.WithStreamServerChain(\n\t\t\t\t\tgrpc_auth.StreamServerInterceptor(testAuthFunc),\n\t\t\t\t),\n\t\t\t)\n\n\t\t\tpb.RegisterGreeterService(grpcServer, &pb.GreeterService{SayHello: SayHelloAuthenticated})\n\n\t\t\tl, err := net.Listen(\"tcp\", \"127.0.0.1:0\")\n\t\t\trequire.NoError(t, err)\n\t\t\tdefer l.Close()\n\n\t\t\tcc, err := grpc.DialContext(ctx, l.Addr().String(), []grpc.DialOption{\n\t\t\t\tgrpc.WithPerRPCCredentials(NewUnsecureSimpleAuthAccess(\"bearer\", tc.token)),\n\t\t\t\tgrpc.WithInsecure(), // TODO: remove this, enforce security\n\t\t\t}...)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tgo grpcServer.Serve(l)\n\n\t\t\tclient := pb.NewGreeterClient(cc)\n\t\t\tres, err := client.SayHello(ctx, &pb.HelloRequest{})\n\t\t\tif tc.assertFunc(t, err) && err == nil {\n\t\t\t\tassert.Equal(t, \"pong authenticated\", res.Message)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/ipfsutil/collector_bandwidth.go",
    "content": "package ipfsutil\n\nimport (\n\tmetrics \"github.com/libp2p/go-libp2p/core/metrics\"\n\tprometheus \"github.com/prometheus/client_golang/prometheus\"\n)\n\nvar (\n\tprotocolsBandwidthInDesc = prometheus.NewDesc(\n\t\tprometheus.BuildFQName(\"ipfs\", \"bandwidth\", \"in\"),\n\t\t\"protocol bandwidth in\",\n\t\t[]string{\"protocol_id\"}, nil,\n\t)\n\tprotocolsBandwidthOutDesc = prometheus.NewDesc(\n\t\tprometheus.BuildFQName(\"ipfs\", \"bandwidth\", \"out\"),\n\t\t\"protocol bandwidth out\",\n\t\t[]string{\"protocol_id\"}, nil,\n\t)\n)\n\n// BandwidthCollector is a prometheus.Collector\nvar _ prometheus.Collector = (*BandwidthCollector)(nil)\n\ntype BandwidthCollector struct {\n\treporter *metrics.BandwidthCounter\n}\n\nfunc NewBandwidthCollector(reporter *metrics.BandwidthCounter) *BandwidthCollector {\n\treturn &BandwidthCollector{reporter}\n}\n\nfunc (bc *BandwidthCollector) Collect(cmetric chan<- prometheus.Metric) {\n\tfor p, s := range bc.reporter.GetBandwidthByProtocol() {\n\t\tif p == \"\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tcmetric <- prometheus.MustNewConstMetric(\n\t\t\tprotocolsBandwidthInDesc,\n\t\t\tprometheus.GaugeValue,\n\t\t\ts.RateIn, string(p))\n\n\t\tcmetric <- prometheus.MustNewConstMetric(\n\t\t\tprotocolsBandwidthOutDesc,\n\t\t\tprometheus.GaugeValue,\n\t\t\ts.RateOut, string(p))\n\t}\n}\n\nfunc (bc *BandwidthCollector) Describe(ch chan<- *prometheus.Desc) {\n\tprometheus.DescribeByCollect(bc, ch)\n}\n"
  },
  {
    "path": "pkg/ipfsutil/collector_host.go",
    "content": "package ipfsutil\n\nimport (\n\thost \"github.com/libp2p/go-libp2p/core/host\"\n\t\"github.com/libp2p/go-libp2p/core/network\"\n\t\"github.com/libp2p/go-libp2p/core/protocol\"\n\tma \"github.com/multiformats/go-multiaddr\"\n\tprometheus \"github.com/prometheus/client_golang/prometheus\"\n)\n\nvar (\n\tprotocolsStreamsSumDesc = prometheus.NewDesc(\n\t\tprometheus.BuildFQName(\"ipfs\", \"host\", \"open_stream\"),\n\t\t\"number of open stream for this protocol\",\n\t\t[]string{\"protocol_id\"}, nil,\n\t)\n\tconnsSumOpts = prometheus.GaugeOpts{\n\t\tName: prometheus.BuildFQName(\"ipfs\", \"host\", \"open_connection\"),\n\t\tHelp: \"number of opened connections\",\n\t}\n\t// protocolStreamDurationOpts = prometheus.HistogramOpts{\n\t// \tName:    prometheus.BuildFQName(\"ipfs\", \"host\", \"stream_duration\"),\n\t// \tHelp:    \"stream duration\",\n\t// \tBuckets: prometheus.LinearBuckets(0, 10, 6),\n\t// }\n)\n\nconst UnknownProtocol = \"UnknownProtocol\"\n\ntype HostCollector struct {\n\thost           host.Host\n\tconnsCollector prometheus.Gauge\n\t// streamsCollector *prometheus.HistogramVec\n}\n\nfunc NewHostCollector(h host.Host) *HostCollector {\n\tgconns := prometheus.NewGauge(connsSumOpts)\n\t// hstreams := prometheus.NewHistogramVec(protocolStreamDurationOpts, []string{\"protocol_id\"})\n\n\tcc := &HostCollector{\n\t\thost:           h,\n\t\tconnsCollector: gconns,\n\t\t// streamsCollector: hstreams,\n\t}\n\th.Network().Notify(cc)\n\treturn cc\n}\n\nfunc (cc *HostCollector) Collect(cmetric chan<- prometheus.Metric) {\n\tcc.connsCollector.Collect(cmetric)\n\t// cc.streamsCollector.Collect(cmetric)\n\n\tstreamsMap := make(map[protocol.ID]int)\n\tfor _, c := range cc.host.Network().Conns() {\n\t\tfor _, s := range c.GetStreams() {\n\t\t\tif s.Protocol() != \"\" {\n\t\t\t\tstreamsMap[s.Protocol()]++\n\t\t\t} else {\n\t\t\t\tstreamsMap[UnknownProtocol]++\n\t\t\t}\n\t\t}\n\t}\n\n\tfor p, ns := range streamsMap {\n\t\tcmetric <- prometheus.MustNewConstMetric(\n\t\t\tprotocolsStreamsSumDesc,\n\t\t\tprometheus.GaugeValue,\n\t\t\tfloat64(ns), string(p))\n\t}\n}\n\nfunc (cc *HostCollector) Describe(ch chan<- *prometheus.Desc) {\n\tch <- protocolsStreamsSumDesc\n\n\tcc.connsCollector.Describe(ch)\n\t// cc.streamsCollector.Describe(ch)\n}\n\nfunc (cc *HostCollector) Listen(network.Network, ma.Multiaddr)      {}\nfunc (cc *HostCollector) ListenClose(network.Network, ma.Multiaddr) {}\n\nfunc (cc *HostCollector) Connected(network.Network, network.Conn) {\n\tcc.connsCollector.Inc()\n}\n\nfunc (cc *HostCollector) Disconnected(network.Network, network.Conn) {\n\tcc.connsCollector.Dec()\n}\n\nfunc (cc *HostCollector) OpenedStream(network.Network, network.Stream) {}\nfunc (cc *HostCollector) ClosedStream(network.Network, network.Stream) {\n\t// elpased := time.Since(s.Stat().Opened)\n\t// cc.streamsCollector.WithLabelValues(string(s.Protocol())).Observe(elpased.Seconds())\n}\n"
  },
  {
    "path": "pkg/ipfsutil/conn_logger.go",
    "content": "package ipfsutil\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/libp2p/go-libp2p/core/host\"\n\t\"github.com/libp2p/go-libp2p/core/network\"\n\t\"github.com/libp2p/go-libp2p/core/peer\"\n\tma \"github.com/multiformats/go-multiaddr\"\n\t\"go.uber.org/zap\"\n\n\t\"berty.tech/weshnet/v2/pkg/logutil\"\n)\n\nvar ignoredTags = map[string]bool{\n\t\"kbucket\":          true,\n\t\"relay\":            true,\n\t\"relay-hop-stream\": true,\n}\n\ntype connLogger struct {\n\thost   host.Host\n\tlogger *zap.Logger\n}\n\nfunc EnableConnLogger(ctx context.Context, logger *zap.Logger, h host.Host) {\n\tnotifee := &connLogger{\n\t\thost:   h,\n\t\tlogger: logger.Named(\"conn_logger\"),\n\t}\n\n\th.Network().Notify(notifee)\n\n\tgo func() {\n\t\t<-ctx.Done()\n\t\th.Network().StopNotify(notifee)\n\t}()\n}\n\nfunc (cl *connLogger) getPeerTags(p peer.ID) []string {\n\tif tagInfo := cl.host.ConnManager().GetTagInfo(p); tagInfo != nil {\n\t\tvar tags []string\n\n\t\tfor tag := range tagInfo.Tags {\n\t\t\tif !ignoredTags[tag] {\n\t\t\t\ttags = append(tags, tag)\n\t\t\t}\n\t\t}\n\n\t\tif len(tags) > 0 {\n\t\t\treturn tags\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (cl *connLogger) Listen(_ network.Network, m ma.Multiaddr) {\n\tcl.logger.Debug(\"Listener opened\", logutil.PrivateString(\"Multiaddr\", m.String()))\n}\n\nfunc (cl *connLogger) ListenClose(_ network.Network, m ma.Multiaddr) {\n\tcl.logger.Debug(\"Listener closed\", logutil.PrivateString(\"Multiaddr\", m.String()))\n}\n\nfunc (cl *connLogger) Connected(_ network.Network, c network.Conn) {\n\t// Wait 10 ms until the peer has been tagged by orbit-db\n\tgo func() {\n\t\t<-time.After(10 * time.Millisecond)\n\t\tif tags := cl.getPeerTags(c.RemotePeer()); tags != nil {\n\t\t\tcl.logger.Info(\"Connected\",\n\t\t\t\tlogutil.PrivateString(\"peer\", c.RemotePeer().String()),\n\t\t\t\tlogutil.PrivateString(\"to\", c.LocalMultiaddr().String()),\n\t\t\t\tlogutil.PrivateString(\"from\", c.RemoteMultiaddr().String()),\n\t\t\t\tlogutil.PrivateStrings(\"tags\", tags),\n\t\t\t)\n\t\t}\n\t}()\n}\n\nfunc (cl *connLogger) Disconnected(_ network.Network, c network.Conn) {\n\tif tags := cl.getPeerTags(c.RemotePeer()); tags != nil {\n\t\tcl.logger.Info(\"Disconnected\",\n\t\t\tlogutil.PrivateString(\"peer\", c.RemotePeer().String()),\n\t\t\tlogutil.PrivateString(\"to\", c.LocalMultiaddr().String()),\n\t\t\tlogutil.PrivateString(\"from\", c.RemoteMultiaddr().String()),\n\t\t\tlogutil.PrivateStrings(\"tags\", tags),\n\t\t)\n\t}\n}\n\nfunc (cl *connLogger) OpenedStream(_ network.Network, s network.Stream) {\n\tif tags := cl.getPeerTags(s.Conn().RemotePeer()); tags != nil {\n\t\tcl.logger.Debug(\"Stream opened\",\n\t\t\tlogutil.PrivateString(\"peer\", s.Conn().RemotePeer().String()),\n\t\t\tlogutil.PrivateString(\"to\", s.Conn().LocalMultiaddr().String()),\n\t\t\tlogutil.PrivateString(\"from\", s.Conn().RemoteMultiaddr().String()),\n\t\t\tlogutil.PrivateString(\"protocol\", string(s.Protocol())),\n\t\t\tlogutil.PrivateStrings(\"tags\", tags),\n\t\t)\n\t}\n}\n\nfunc (cl *connLogger) ClosedStream(_ network.Network, s network.Stream) {\n\tif tags := cl.getPeerTags(s.Conn().RemotePeer()); tags != nil {\n\t\tcl.logger.Debug(\"Stream closed\",\n\t\t\tlogutil.PrivateString(\"peer\", s.Conn().RemotePeer().String()),\n\t\t\tlogutil.PrivateString(\"to\", s.Conn().LocalMultiaddr().String()),\n\t\t\tlogutil.PrivateString(\"from\", s.Conn().RemoteMultiaddr().String()),\n\t\t\tlogutil.PrivateString(\"protocol\", string(s.Protocol())),\n\t\t\tlogutil.PrivateStrings(\"tags\", tags),\n\t\t)\n\t}\n}\n"
  },
  {
    "path": "pkg/ipfsutil/conn_manager.go",
    "content": "package ipfsutil\n\nimport (\n\t\"fmt\"\n\t\"sync\"\n\n\t\"github.com/libp2p/go-libp2p/core/connmgr\"\n\t\"github.com/libp2p/go-libp2p/core/event\"\n\t\"github.com/libp2p/go-libp2p/core/peer\"\n\t\"go.uber.org/zap\"\n)\n\ntype TypeTagAction int\n\nconst (\n\tTypeTagActionTag TypeTagAction = iota\n\tTypeTagActionUntag\n\tTypeTagActionUpsert\n)\n\ntype EvtPeerTag struct {\n\tKind TypeTagAction\n\n\tPeer  peer.ID\n\tTag   string\n\tDiff  int\n\tTotal int\n}\n\nvar _ connmgr.ConnManager = (*BertyConnManager)(nil)\n\n// keep track of peer of interest\ntype BertyConnManager struct {\n\tconnmgr.ConnManager\n\n\tlogger     *zap.Logger\n\ttagEmitter event.Emitter\n\tmuMarked   sync.RWMutex\n\tmarked     map[peer.ID]int\n}\n\nfunc NewBertyConnManager(logger *zap.Logger, cm connmgr.ConnManager) *BertyConnManager {\n\treturn &BertyConnManager{\n\t\tConnManager: cm,\n\t\tlogger:      logger,\n\t\tmarked:      make(map[peer.ID]int),\n\t}\n}\n\nfunc (c *BertyConnManager) RegisterEventBus(bus event.Bus) (err error) {\n\tif c.tagEmitter == nil {\n\t\tc.tagEmitter, err = bus.Emitter(new(EvtPeerTag))\n\t} else {\n\t\terr = fmt.Errorf(\"event emitter already registered\")\n\t}\n\treturn err\n}\n\nfunc (c *BertyConnManager) GetPeerScore(p peer.ID) (score int, exist bool) {\n\tc.muMarked.RLock()\n\tscore, exist = c.marked[p]\n\tc.muMarked.RUnlock()\n\treturn\n}\n\n// TagPeer tags a peer with a string, associating a weight with the tag.\nfunc (c *BertyConnManager) TagPeer(p peer.ID, tag string, score int) {\n\tc.ConnManager.TagPeer(p, tag, score)\n\n\told, total := c.computePeerScore(p)\n\n\tif old != total && c.tagEmitter != nil {\n\t\tevt := EvtPeerTag{\n\t\t\tKind:  TypeTagActionTag,\n\t\t\tPeer:  p,\n\t\t\tTag:   tag,\n\t\t\tDiff:  total - old,\n\t\t\tTotal: total,\n\t\t}\n\n\t\tif err := c.tagEmitter.Emit(evt); err != nil {\n\t\t\tc.logger.Error(\"unable to emit tag event\", zap.Error(err))\n\t\t}\n\t}\n}\n\n// Untag removes the tagged value from the peer.\nfunc (c *BertyConnManager) UntagPeer(p peer.ID, tag string) {\n\tc.ConnManager.UntagPeer(p, tag)\n\n\told, total := c.computePeerScore(p)\n\n\tif old != total && c.tagEmitter != nil {\n\t\tevt := EvtPeerTag{\n\t\t\tKind:  TypeTagActionUntag,\n\t\t\tPeer:  p,\n\t\t\tTag:   tag,\n\t\t\tDiff:  total - old,\n\t\t\tTotal: total,\n\t\t}\n\n\t\tif err := c.tagEmitter.Emit(evt); err != nil {\n\t\t\tc.logger.Error(\"unable to emit tag event\", zap.Error(err))\n\t\t}\n\t}\n}\n\n// UpsertTag updates an existing tag or inserts a new one.\n\n// The connection manager calls the upsert function supplying the current\n// value of the tag (or zero if inexistent). The return value is used as\n// the new value of the tag.\nfunc (c *BertyConnManager) UpsertTag(p peer.ID, tag string, upsert func(int) int) {\n\tc.ConnManager.UpsertTag(p, tag, upsert)\n\n\told, total := c.computePeerScore(p)\n\n\tif old != total && c.tagEmitter != nil {\n\t\tevt := EvtPeerTag{\n\t\t\tKind:  TypeTagActionUpsert,\n\t\t\tPeer:  p,\n\t\t\tTag:   tag,\n\t\t\tDiff:  total - old,\n\t\t\tTotal: total,\n\t\t}\n\n\t\tif err := c.tagEmitter.Emit(evt); err != nil {\n\t\t\tc.logger.Error(\"unable to emit tag event\", zap.Error(err))\n\t\t}\n\t}\n}\n\nfunc (c *BertyConnManager) computePeerScore(p peer.ID) (old, newScore int) {\n\tc.muMarked.Lock()\n\n\told = c.marked[p]\n\tif info := c.ConnManager.GetTagInfo(p); info != nil {\n\t\tif newScore = info.Value; newScore > 0 {\n\t\t\tc.marked[p] = newScore\n\t\t} else {\n\t\t\tdelete(c.marked, p)\n\t\t}\n\t}\n\n\tc.muMarked.Unlock()\n\treturn\n}\n"
  },
  {
    "path": "pkg/ipfsutil/consts.go",
    "content": "package ipfsutil\n\nconst (\n\t// svc ams 1\n\tDefaultP2PRdvpMaddr = \"/ip4/51.15.25.224/udp/4040/quic-v1/p2p/12D3KooWHhDBv6DJJ4XDWjzEXq6sVNEs6VuxsV1WyBBEhPENHzcZ\"\n\t// svc ams 1\n\tDefaultP2PStaticRelay = \"/ip4/51.15.25.224/udp/6363/quic-v1/p2p/12D3KooWAHcEz4K5XAgRDav9fLuhiRY2wuXip385EmT5RoRkCmjr\"\n)\n"
  },
  {
    "path": "pkg/ipfsutil/doc.go",
    "content": "// Package ipfsutil contains helpers around IPFS (logging, datastore, networking, core API, ...).\npackage ipfsutil\n"
  },
  {
    "path": "pkg/ipfsutil/extended_core_api.go",
    "content": "package ipfsutil\n\nimport (\n\tipfs_core \"github.com/ipfs/kubo/core\"\n\tipfs_coreapi \"github.com/ipfs/kubo/core/coreapi\"\n\tcoreiface \"github.com/ipfs/kubo/core/coreiface\"\n\t\"github.com/libp2p/go-libp2p/core/connmgr\"\n\tipfs_host \"github.com/libp2p/go-libp2p/core/host\"\n)\n\ntype ConnMgr interface {\n\tconnmgr.ConnManager\n}\n\ntype ExtendedCoreAPI interface {\n\tcoreiface.CoreAPI\n\tipfs_host.Host\n\n\tConnMgr() ConnMgr\n}\n\ntype extendedCoreAPI struct {\n\tcoreiface.CoreAPI\n\tipfs_host.Host\n}\n\nfunc (e *extendedCoreAPI) ConnMgr() ConnMgr {\n\treturn e.Host.ConnManager()\n}\n\nfunc NewExtendedCoreAPI(host ipfs_host.Host, api coreiface.CoreAPI) ExtendedCoreAPI {\n\treturn &extendedCoreAPI{\n\t\tCoreAPI: api,\n\t\tHost:    host,\n\t}\n}\n\nfunc NewExtendedCoreAPIFromNode(node *ipfs_core.IpfsNode) (ExtendedCoreAPI, error) {\n\tapi, err := ipfs_coreapi.NewCoreAPI(node)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn NewExtendedCoreAPI(node.PeerHost, api), nil\n}\n"
  },
  {
    "path": "pkg/ipfsutil/helpers.go",
    "content": "package ipfsutil\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/libp2p/go-libp2p/core/network\"\n\t\"github.com/libp2p/go-libp2p/core/peer\"\n\tma \"github.com/multiformats/go-multiaddr\"\n\tmadns \"github.com/multiformats/go-multiaddr-dns\"\n\t\"go.uber.org/multierr\"\n\t\"go.uber.org/zap\"\n\n\t\"berty.tech/weshnet/v2/pkg/logutil\"\n)\n\nfunc ParseAndResolveIpfsAddr(ctx context.Context, addr string) (*peer.AddrInfo, error) {\n\tmaddr, err := ma.NewMultiaddr(addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !madns.Matches(maddr) {\n\t\treturn peer.AddrInfoFromP2pAddr(maddr)\n\t}\n\n\taddrs, err := madns.Resolve(ctx, maddr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif len(addrs) == 0 {\n\t\treturn nil, errors.New(\"fail to resolve the multiaddr:\" + maddr.String())\n\t}\n\n\tvar info peer.AddrInfo\n\tfor _, addr := range addrs {\n\t\ttaddr, id := peer.SplitAddr(addr)\n\t\tif id == \"\" {\n\t\t\t// not an ipfs addr, skipping.\n\t\t\tcontinue\n\t\t}\n\t\tswitch info.ID {\n\t\tcase \"\":\n\t\t\tinfo.ID = id\n\t\tcase id:\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\n\t\t\t\t\"ambiguous maddr %s could refer to %s or %s\",\n\t\t\t\tmaddr,\n\t\t\t\tinfo.ID,\n\t\t\t\tid,\n\t\t\t)\n\t\t}\n\t\tinfo.Addrs = append(info.Addrs, taddr)\n\t}\n\treturn &info, nil\n}\n\nfunc ParseAndResolveMaddrs(ctx context.Context, logger *zap.Logger, addrs []string) ([]*peer.AddrInfo, error) {\n\t// Resolve all addresses\n\toutPeersUnmatched := make([]*peer.AddrInfo, len(addrs))\n\tvar (\n\t\terrs    error\n\t\toutLock sync.Mutex\n\t\twg      sync.WaitGroup\n\t)\n\twg.Add(len(addrs))\n\tfor i, v := range addrs {\n\t\tgo func(j int, addr string) {\n\t\t\tdefer wg.Done()\n\n\t\t\trdvpeer, err := ParseAndResolveIpfsAddr(ctx, addr)\n\t\t\tif err != nil {\n\t\t\t\toutLock.Lock()\n\t\t\t\tdefer outLock.Unlock()\n\t\t\t\terrs = multierr.Append(errs, err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\taddrStrings := make([]string, len(rdvpeer.Addrs))\n\t\t\tfor i, maddr := range rdvpeer.Addrs {\n\t\t\t\taddrStrings[i] = maddr.String()\n\t\t\t}\n\t\t\tlogger.Debug(\"rdvp peer resolved addrs\",\n\t\t\t\tlogutil.PrivateString(\"input\", addr),\n\t\t\t\t// logutil.PrivateString(\"ID\", rdvpeer.ID.Pretty()),\n\t\t\t\tlogutil.PrivateStrings(\"addrs\", addrStrings),\n\t\t\t)\n\t\t\toutPeersUnmatched[j] = rdvpeer\n\t\t}(i, v)\n\t}\n\twg.Wait()\n\tif errs != nil {\n\t\treturn nil, errs\n\t}\n\t// Match peers by ID\n\toutPeersMatched := make(map[peer.ID][]ma.Multiaddr)\n\tfor _, v := range outPeersUnmatched {\n\t\toutPeersMatched[v.ID] = append(outPeersMatched[v.ID], v.Addrs...)\n\t}\n\n\t// Create the ultimate *peer.AddrInfo\n\tvar outPeers []*peer.AddrInfo\n\tfor id, maddrs := range outPeersMatched {\n\t\toutPeers = append(outPeers, &peer.AddrInfo{\n\t\t\tID:    id,\n\t\t\tAddrs: maddrs,\n\t\t})\n\t}\n\n\treturn outPeers, nil\n}\n\nvar ErrExpectedEOF = errors.New(\"red data when expecting EOF\")\n\nconst DefaultCloseTimeout = time.Second * 5\n\nfunc FullClose(s network.Stream) error {\n\t// Start the close.\n\terr := s.CloseWrite()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// We don't want to wait indefinitely\n\t_ = s.SetDeadline(time.Now().Add(DefaultCloseTimeout))\n\n\t// Trying with a long slice to fetch `n`.\n\tn, err := s.Read([]byte{0})\n\tif n > 0 || err == nil {\n\t\t_ = s.Reset()\n\t\treturn ErrExpectedEOF\n\t}\n\tif err == io.EOF {\n\t\treturn nil\n\t}\n\t_ = s.Reset()\n\treturn err\n}\n"
  },
  {
    "path": "pkg/ipfsutil/helpers_test.go",
    "content": "package ipfsutil\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/hex\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\tmrand \"math/rand\"\n\t\"testing\"\n\t\"time\"\n\n\tp2p \"github.com/libp2p/go-libp2p\"\n\t\"github.com/libp2p/go-libp2p/core/crypto\"\n\t\"github.com/libp2p/go-libp2p/core/host\"\n\t\"github.com/libp2p/go-libp2p/core/network\"\n\t\"github.com/libp2p/go-libp2p/core/peerstore\"\n\t\"github.com/libp2p/go-libp2p/core/pnet\"\n\ttcp \"github.com/libp2p/go-libp2p/p2p/transport/tcp\"\n\t\"moul.io/srand\"\n)\n\nconst closeTestPid = \"/testing/close/0.1.0\"\n\n// TestFullClose creates 2 hosts (a and b). a will dial b and will then try to close the connection using FullClose.\n// b will play various scenario to check that it's working fine.\nfunc TestFullClose(t *testing.T) {\n\t// Creating ctx\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\tvar a, b host.Host\n\t{\n\t\t// Creating a private network\n\t\tseed, err := srand.Secure()\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"failed to fetch secure source: %s\", err)\n\t\t}\n\n\t\tprov := mrand.New(mrand.NewSource(seed))\n\n\t\tpskKey := make([]byte, 32)\n\t\t// math/rand#Rand.Read can't ever fail nor return an n != len(buf)\n\t\t_, _ = prov.Read(pskKey)\n\n\t\t// Generating PSK key, pulled from https://github.com/Kubuxu/go-ipfs-swarm-key-gen/blob/0ee739ec6d322bc1892999882e4738270e97b181/ipfs-swarm-key-gen/main.go#L15-L17\n\t\tpsk, err := pnet.DecodeV1PSK(bytes.NewReader([]byte(\"/key/swarm/psk/1.0.0/\\n/base16/\\n\" + hex.EncodeToString(pskKey))))\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"failed to create PSK: %s\", err)\n\t\t}\n\n\t\t// Creating the hosts\n\t\tpriv, _, err := crypto.GenerateEd25519Key(prov)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"failed to generate A's private key: %s\", err)\n\t\t}\n\n\t\ta, err = p2p.New(p2p.DisableRelay(),\n\t\t\tp2p.Transport(tcp.NewTCPTransport),\n\t\t\tp2p.Identity(priv),\n\t\t\tp2p.PrivateNetwork(psk),\n\t\t\tp2p.ListenAddrStrings(\"/ip4/127.0.0.1/tcp/0\"),\n\t\t)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"failed to create host A: %s\", err)\n\t\t}\n\t\tpriv, _, err = crypto.GenerateEd25519Key(prov)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"failed to generate B's private key: %s\", err)\n\t\t}\n\n\t\tb, err = p2p.New(p2p.DisableRelay(),\n\t\t\tp2p.Transport(tcp.NewTCPTransport),\n\t\t\tp2p.Identity(priv),\n\t\t\tp2p.PrivateNetwork(psk),\n\t\t\tp2p.ListenAddrStrings(\"/ip4/127.0.0.1/tcp/0\"),\n\t\t)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"failed to create host B: %s\", err)\n\t\t}\n\t}\n\n\t// Adding the hosts together\n\ta.Peerstore().SetAddrs(b.ID(), b.Addrs(), peerstore.PermanentAddrTTL)\n\tb.Peerstore().SetAddrs(a.ID(), a.Addrs(), peerstore.PermanentAddrTTL)\n\n\t// First scenario, regular close\n\t{\n\t\terrcb := make(chan error)\n\n\t\tb.SetStreamHandler(closeTestPid, func(s network.Stream) {\n\t\t\tn, err := s.Read([]byte{0})  // Trying to read, should io.EOF\n\t\t\tif n == 0 && err == io.EOF { // Good\n\t\t\t\terr = s.Close() // Trying to close ourself\n\t\t\t\tif err == nil {\n\t\t\t\t\terrcb <- io.EOF // Perfect\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\terrcb <- fmt.Errorf(\"error closing after EOF: %w\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif err != nil {\n\t\t\t\terrcb <- fmt.Errorf(\"error not EOF while reading: %w\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// n > 0\n\t\t\terrcb <- errors.New(\"n > 0, expected EOF\")\n\t\t})\n\n\t\terrca := make(chan error)\n\t\t// Dialing, we expect a fast close.\n\t\tgo func() {\n\t\t\ts, err := a.NewStream(ctx, b.ID(), closeTestPid)\n\t\t\tif err != nil {\n\t\t\t\terrca <- fmt.Errorf(\"failed to create stream: %w\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\terrca <- FullClose(s)\n\t\t}()\n\n\t\ttimec := time.After(DefaultCloseTimeout / 2) // Fast close must resolve in max half of the timeout time.\n\t\tvar done uint = 2\n\t\tfor done > 0 {\n\t\t\tselect {\n\t\t\tcase err := <-errca:\n\t\t\t\tif err == nil || err == io.EOF {\n\t\t\t\t\tdone--\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tt.Fatalf(\"error for A while fast close: %s\", err)\n\t\t\tcase err := <-errcb:\n\t\t\t\tif err == nil || err == io.EOF {\n\t\t\t\t\tdone--\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tt.Fatalf(\"error for B while fast close: %s\", err)\n\t\t\tcase <-timec:\n\t\t\t\tt.Fatal(\"fast close took too long.\")\n\t\t\t}\n\t\t}\n\t}\n\n\t// Second scenario, regular timeout\n\t{\n\t\terrcb := make(chan error)\n\n\t\tvar gs network.Stream // Prevent running the terminator\n\t\tb.SetStreamHandler(closeTestPid, func(s network.Stream) {\n\t\t\tgs = s // Thread unsafe if we have concurrent streams incoming, shouldn't happen thx to the pnet.\n\t\t\t_, err := s.Read([]byte{0})\n\t\t\terrcb <- err\n\t\t})\n\n\t\terrca := make(chan error)\n\t\t// Dialing, we expect a timeout.\n\t\tgo func() {\n\t\t\ts, err := a.NewStream(ctx, b.ID(), closeTestPid)\n\t\t\tif err != nil {\n\t\t\t\terrca <- fmt.Errorf(\"failed to create stream: %w\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\terrca <- FullClose(s)\n\t\t}()\n\n\t\ttimec := time.After(DefaultCloseTimeout + time.Second) // Timeout must resolve in timeout time + 1s.\n\t\tvar done uint = 2\n\t\tfor done > 0 {\n\t\t\tselect {\n\t\t\tcase err := <-errca:\n\t\t\t\t// i/o deadline reached must be checked msg.\n\t\t\t\tif err == nil || err.Error() == \"i/o deadline reached\" {\n\t\t\t\t\tdone--\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tt.Fatalf(\"error for A while slow close: %s\", err)\n\t\t\tcase err := <-errcb:\n\t\t\t\tif err == nil || err == io.EOF {\n\t\t\t\t\tdone--\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tt.Fatalf(\"error for B while slow close: %s\", err)\n\t\t\tcase <-timec:\n\t\t\t\tt.Fatal(\"slow close took too long.\")\n\t\t\t}\n\t\t}\n\t\t_ = gs // Prevent running the GC Terminator.\n\t}\n}\n"
  },
  {
    "path": "pkg/ipfsutil/keystore_datastore.go",
    "content": "package ipfsutil\n\nimport (\n\t\"context\"\n\n\tdatastore \"github.com/ipfs/go-datastore\"\n\tkeystore \"github.com/ipfs/go-ipfs-keystore\"\n\t\"github.com/libp2p/go-libp2p/core/crypto\"\n\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n)\n\ntype datastoreKeystore struct {\n\tds datastore.Datastore\n}\n\nfunc (k *datastoreKeystore) Has(name string) (bool, error) {\n\treturn k.ds.Has(context.TODO(), datastore.NewKey(name))\n}\n\nfunc (k *datastoreKeystore) Put(name string, key crypto.PrivKey) error {\n\tbytes, err := crypto.MarshalPrivateKey(key)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn k.ds.Put(context.TODO(), datastore.NewKey(name), bytes)\n}\n\nfunc (k *datastoreKeystore) Get(name string) (crypto.PrivKey, error) {\n\tbytes, err := k.ds.Get(context.TODO(), datastore.NewKey(name))\n\tif err == datastore.ErrNotFound {\n\t\treturn nil, keystore.ErrNoSuchKey\n\t} else if err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn crypto.UnmarshalPrivateKey(bytes)\n}\n\nfunc (k *datastoreKeystore) Delete(name string) error {\n\treturn k.ds.Delete(context.TODO(), datastore.NewKey(name))\n}\n\nfunc (k *datastoreKeystore) List() ([]string, error) {\n\treturn nil, errcode.ErrCode_ErrNotImplemented\n}\n\nfunc NewDatastoreKeystore(ds datastore.Datastore) keystore.Keystore {\n\treturn &datastoreKeystore{\n\t\tds: ds,\n\t}\n}\n"
  },
  {
    "path": "pkg/ipfsutil/lifecycle.go",
    "content": "package ipfsutil\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/libp2p/go-libp2p/core/connmgr\"\n\thost \"github.com/libp2p/go-libp2p/core/host\"\n\t\"github.com/libp2p/go-libp2p/core/peer\"\n\t\"github.com/libp2p/go-libp2p/p2p/host/eventbus\"\n\t\"github.com/libp2p/go-libp2p/p2p/protocol/ping\"\n\t\"go.uber.org/zap\"\n\n\t\"berty.tech/weshnet/v2/pkg/lifecycle\"\n\t\"berty.tech/weshnet/v2/pkg/logutil\"\n\t\"berty.tech/weshnet/v2/pkg/netmanager\"\n)\n\nvar (\n\tConnLifecycleGracePeriod   = time.Second\n\tConnLifecyclePingTimeout   = time.Second * 5\n\tConnPeerOfInterestMinScore = 20\n)\n\ntype ConnLifecycle struct {\n\tconnmgr.ConnManager\n\n\trootCtx context.Context\n\n\tlogger *zap.Logger\n\n\tpeering *PeeringService\n\tps      *ping.PingService\n\th       host.Host\n\tlm      *lifecycle.Manager\n}\n\nfunc NewConnLifecycle(ctx context.Context, logger *zap.Logger, h host.Host, ps *PeeringService, lm *lifecycle.Manager, net *netmanager.NetManager) (*ConnLifecycle, error) {\n\tcl := &ConnLifecycle{\n\t\tpeering: ps,\n\t\trootCtx: ctx,\n\t\tlogger:  logger,\n\t\tps:      ping.NewPingService(h),\n\t\th:       h,\n\t\tlm:      lm,\n\t}\n\n\t// start peer of interest monitoring process\n\tif err := cl.monitorPeerOfInterest(ctx); err != nil {\n\t\treturn nil, err\n\t}\n\n\t// start app state monitoring process\n\tgo cl.monitorAppState(ctx)\n\n\tcl.logger.Debug(\"lifecycle conn started\")\n\n\tgo func() {\n\t\tcurrentState := net.GetCurrentState()\n\n\t\tfor {\n\t\t\tok, _ := net.WaitForStateChange(ctx, &currentState, netmanager.ConnectivityStateChanged)\n\n\t\t\tif !ok {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tcurrentState = net.GetCurrentState()\n\n\t\t\tif net.GetCurrentState().State == netmanager.ConnectivityStateOn {\n\t\t\t\tgo cl.dropUnavailableConn()\n\t\t\t}\n\t\t}\n\t}()\n\n\treturn cl, nil\n}\n\nfunc (cl *ConnLifecycle) monitorAppState(ctx context.Context) {\n\tcurrentState := lifecycle.StateActive\n\tfor {\n\t\tstart := time.Now()\n\t\tif !cl.lm.WaitForStateChange(ctx, currentState) {\n\t\t\treturn\n\t\t}\n\t\tcurrentState = cl.lm.GetCurrentState()\n\n\t\tif time.Since(start) <= ConnLifecycleGracePeriod {\n\t\t\tcontinue\n\t\t}\n\n\t\tswitch currentState {\n\t\tcase lifecycle.StateInactive:\n\t\t\tcl.logger.Debug(\"inactive mode\")\n\t\tcase lifecycle.StateActive:\n\t\t\tcl.logger.Debug(\"active mode\")\n\t\t\tgo cl.dropUnavailableConn()\n\t\t}\n\t}\n}\n\nfunc (cl *ConnLifecycle) dropUnavailableConn() {\n\tcl.logger.Debug(\"dropping unavailable conn\")\n\n\tpeers := make(map[peer.ID]struct{})\n\tfor _, c := range cl.h.Network().Conns() {\n\t\tif _, ok := peers[c.RemotePeer()]; !ok {\n\t\t\tpeers[c.RemotePeer()] = struct{}{}\n\t\t}\n\t}\n\n\tunavailable := uint32(0)\n\twg := sync.WaitGroup{}\n\tctx, cancel := context.WithCancel(cl.rootCtx)\n\tfor p := range peers {\n\t\tcping := cl.ps.Ping(ctx, p)\n\t\twg.Add(1)\n\t\tgo func(peer peer.ID) {\n\t\t\tdefer wg.Done()\n\n\t\t\tselect {\n\t\t\tcase ret := <-cping:\n\t\t\t\tif ret.Error == nil {\n\t\t\t\t\treturn // everything should be ok\n\t\t\t\t}\n\t\t\tcase <-time.After(ConnLifecyclePingTimeout):\n\t\t\t}\n\n\t\t\t// connection should be dead\n\t\t\tatomic.AddUint32(&unavailable, 1)\n\n\t\t\t// if we are here, conn should be kill\n\t\t\tif err := cl.h.Network().ClosePeer(peer); err != nil {\n\t\t\t\tcl.logger.Warn(\"unable to close connection\", zap.Error(err))\n\t\t\t}\n\t\t}(p)\n\t}\n\n\twg.Wait()\n\tcancel()\n\n\tif unavailable > 0 {\n\t\tavailable := uint32(len(peers)) - unavailable\n\t\tcl.logger.Debug(\"dropped unavailable peers\", zap.Uint32(\"available\", available), zap.Uint32(\"unavailable\", unavailable))\n\t} else {\n\t\tcl.logger.Debug(\"all peers are available\")\n\t}\n}\n\nfunc (cl *ConnLifecycle) monitorPeerOfInterest(ctx context.Context) error {\n\tsub, err := cl.h.EventBus().Subscribe([]any{\n\t\tnew(EvtPeerTag),\n\t}, eventbus.Name(\"weshnet/lifecycle/monitor-peer-of-interest\"))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to subscribe to `EvtPeerConnectednessChanged`: %w\", err)\n\t}\n\n\tfor _, p := range cl.h.Peerstore().Peers() {\n\t\tif tag := cl.h.ConnManager().GetTagInfo(p); tag != nil && tag.Value >= ConnPeerOfInterestMinScore {\n\t\t\tinfos := cl.h.Peerstore().PeerInfo(p)\n\t\t\tcl.peering.AddPeer(infos)\n\t\t\tcl.logger.Debug(\"adding peer of interest\", logutil.PrivateStringer(\"peer\", p), zap.Int(\"score\", tag.Value))\n\t\t}\n\t}\n\n\tgo func() {\n\t\tdefer sub.Close()\n\t\tfor {\n\t\t\tvar e any\n\t\t\tselect {\n\t\t\tcase e = <-sub.Out():\n\t\t\tcase <-ctx.Done():\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tevt := e.(EvtPeerTag)\n\n\t\t\toldTotal := evt.Total - evt.Diff\n\t\t\tif evt.Total >= ConnPeerOfInterestMinScore && oldTotal < ConnPeerOfInterestMinScore {\n\t\t\t\tinfos := cl.h.Peerstore().PeerInfo(evt.Peer)\n\t\t\t\tcl.peering.AddPeer(infos)\n\t\t\t\tcl.logger.Debug(\"marking peer as peer of interest\",\n\t\t\t\t\tlogutil.PrivateStringer(\"peer\", evt.Peer), zap.Int(\"score\", evt.Total), zap.Int(\"diff\", evt.Diff), zap.String(\"last_tag\", evt.Tag))\n\t\t\t} else if evt.Total < ConnPeerOfInterestMinScore && oldTotal >= ConnPeerOfInterestMinScore {\n\t\t\t\tcl.peering.RemovePeer(evt.Peer)\n\t\t\t\tcl.logger.Debug(\"unmarking peer as peer of interest\",\n\t\t\t\t\tlogutil.PrivateStringer(\"peer\", evt.Peer), zap.Int(\"score\", evt.Total), zap.Int(\"diff\", evt.Diff), zap.String(\"last_tag\", evt.Tag))\n\t\t\t}\n\t\t}\n\t}()\n\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/ipfsutil/localrecord.go",
    "content": "package ipfsutil\n\nimport (\n\t\"context\"\n\t\"os\"\n\n\tipfs_core \"github.com/ipfs/kubo/core\"\n\tcoreiface \"github.com/ipfs/kubo/core/coreiface\"\n\t\"github.com/libp2p/go-libp2p/core/host\"\n\t\"github.com/libp2p/go-libp2p/core/network\"\n\t\"github.com/libp2p/go-libp2p/core/protocol\"\n\tma \"github.com/multiformats/go-multiaddr\"\n\tmafmt \"github.com/multiformats/go-multiaddr-fmt\"\n\tmanet \"github.com/multiformats/go-multiaddr/net\"\n\n\tmc \"berty.tech/weshnet/v2/pkg/multipeer-connectivity-driver\"\n)\n\nconst recProtocolID = protocol.ID(\"wesh/p2p/localrecord\")\n\ntype LocalRecord struct {\n\thost host.Host\n}\n\n// OptionLocalRecord is given to CoreAPIOption.Options when the ipfs node setup\nfunc OptionLocalRecord(node *ipfs_core.IpfsNode, _ coreiface.CoreAPI) error {\n\tlr := &LocalRecord{\n\t\thost: node.PeerHost,\n\t}\n\tlr.host.Network().Notify(lr)\n\tlr.host.SetStreamHandler(recProtocolID, lr.handleLocalRecords)\n\n\treturn nil\n}\n\n// called when network starts listening on an addr\nfunc (lr *LocalRecord) Listen(network.Network, ma.Multiaddr) {}\n\n// called when network stops listening on an addr\nfunc (lr *LocalRecord) ListenClose(network.Network, ma.Multiaddr) {}\n\n// called when a connection opened\nfunc (lr *LocalRecord) Connected(_ network.Network, c network.Conn) {\n\tctx := context.Background() // FIXME: since go-libp2p-core@0.8.0 adds support for passed context on new call, we should think if we have a better context to pass here\n\tgo func() {\n\t\tif manet.IsPrivateAddr(c.RemoteMultiaddr()) || mafmt.Base(mc.ProtocolCode).Matches(c.RemoteMultiaddr()) {\n\t\t\tif err := lr.sendLocalRecord(ctx, c); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}()\n}\n\n// called when a connection closed\nfunc (lr *LocalRecord) Disconnected(network.Network, network.Conn) {}\n\n// called when a stream opened\nfunc (lr *LocalRecord) OpenedStream(network.Network, network.Stream) {}\n\n// called when a stream closed\nfunc (lr *LocalRecord) ClosedStream(network.Network, network.Stream) {}\n\nfunc (lr *LocalRecord) sendLocalRecord(ctx context.Context, c network.Conn) error {\n\ts, err := c.NewStream(ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn s.SetProtocol(recProtocolID)\n}\n\nfunc (lr *LocalRecord) handleLocalRecords(network.Stream) {\n\tos.Stderr.WriteString(\"handleLocalRecords\")\n}\n"
  },
  {
    "path": "pkg/ipfsutil/metrics.go",
    "content": "package ipfsutil\n"
  },
  {
    "path": "pkg/ipfsutil/mobile/host.go",
    "content": "package node\n\nimport (\n\t\"fmt\"\n\n\tipfs_p2p \"github.com/ipfs/kubo/core/node/libp2p\"\n\tp2p \"github.com/libp2p/go-libp2p\"\n\tp2p_host \"github.com/libp2p/go-libp2p/core/host\"\n\tp2p_peer \"github.com/libp2p/go-libp2p/core/peer\"\n\tp2p_pstore \"github.com/libp2p/go-libp2p/core/peerstore\"\n)\n\n// HostMobile is a p2p_host.Host\nvar _ p2p_host.Host = (*HostMobile)(nil)\n\ntype HostConfigFunc func(p2p_host.Host) error\n\n// @TODO: add custom mobile option here\ntype HostConfig struct {\n\t// called after host init\n\tConfigFunc HostConfigFunc\n\n\t// p2p options\n\tOptions []p2p.Option\n}\n\nfunc ChainHostConfig(cfgs ...HostConfigFunc) HostConfigFunc {\n\treturn func(host p2p_host.Host) (err error) {\n\t\tfor _, cfg := range cfgs {\n\t\t\tif cfg == nil {\n\t\t\t\tcontinue // skip empty config\n\t\t\t}\n\n\t\t\tif err = cfg(host); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\treturn\n\t}\n}\n\ntype HostMobile struct {\n\tp2p_host.Host\n}\n\nfunc NewHostConfigOption(hopt ipfs_p2p.HostOption, cfg *HostConfig) ipfs_p2p.HostOption {\n\treturn func(id p2p_peer.ID, ps p2p_pstore.Peerstore, options ...p2p.Option) (p2p_host.Host, error) {\n\t\t// add p2p custom options\n\t\tif cfg.Options != nil {\n\t\t\toptions = append(options, cfg.Options...)\n\t\t}\n\n\t\thost, err := hopt(id, ps, options...)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tif cfg.ConfigFunc != nil {\n\t\t\t// apply host custom config\n\t\t\tif err := cfg.ConfigFunc(host); err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"unable to apply host config: %w\", err)\n\t\t\t}\n\t\t}\n\n\t\treturn host, nil\n\t}\n}\n"
  },
  {
    "path": "pkg/ipfsutil/mobile/node.go",
    "content": "package node\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net\"\n\t\"os\"\n\n\tipfs_oldcmds \"github.com/ipfs/kubo/commands\"\n\tipfs_core \"github.com/ipfs/kubo/core\"\n\tipfs_corehttp \"github.com/ipfs/kubo/core/corehttp\"\n\tipfs_p2p \"github.com/ipfs/kubo/core/node/libp2p\"\n\tp2p_host \"github.com/libp2p/go-libp2p/core/host\"\n)\n\ntype IpfsConfig struct {\n\tHostConfig *HostConfig\n\tHostOption ipfs_p2p.HostOption\n\n\tRoutingConfig *RoutingConfig\n\tRoutingOption ipfs_p2p.RoutingOption\n\n\tRepoMobile *RepoMobile\n\tExtraOpts  map[string]bool\n}\n\nfunc (c *IpfsConfig) fillDefault() error {\n\tif c.RepoMobile == nil {\n\t\treturn fmt.Errorf(\"repo cannot be nil\")\n\t}\n\n\tif c.ExtraOpts == nil {\n\t\tc.ExtraOpts = make(map[string]bool)\n\t}\n\n\tif c.RoutingOption == nil {\n\t\tc.RoutingOption = ipfs_p2p.DHTOption\n\t}\n\n\tif c.RoutingConfig == nil {\n\t\tc.RoutingConfig = &RoutingConfig{}\n\t}\n\n\tif c.HostOption == nil {\n\t\tc.HostOption = ipfs_p2p.DefaultHostOption\n\t}\n\n\tif c.HostConfig == nil {\n\t\tc.HostConfig = &HostConfig{}\n\t}\n\n\treturn nil\n}\n\ntype IpfsMobile struct {\n\t*ipfs_core.IpfsNode\n\tRepo *RepoMobile\n\n\tcommandCtx ipfs_oldcmds.Context\n}\n\nfunc (im *IpfsMobile) PeerHost() p2p_host.Host {\n\treturn im.IpfsNode.PeerHost\n}\n\nfunc (im *IpfsMobile) Close() error {\n\treturn im.IpfsNode.Close()\n}\n\nfunc (im *IpfsMobile) ServeCoreHTTP(l net.Listener, opts ...ipfs_corehttp.ServeOption) error {\n\tgatewayOpt := ipfs_corehttp.GatewayOption(ipfs_corehttp.WebUIPaths...)\n\topts = append(opts,\n\t\tipfs_corehttp.WebUIOption,\n\t\tgatewayOpt,\n\t\tipfs_corehttp.CommandsOption(im.commandCtx),\n\t)\n\n\treturn ipfs_corehttp.Serve(im.IpfsNode, l, opts...)\n}\n\nfunc (im *IpfsMobile) ServeGateway(l net.Listener, opts ...ipfs_corehttp.ServeOption) error {\n\topts = append(opts,\n\t\tipfs_corehttp.HostnameOption(),\n\t\tipfs_corehttp.GatewayOption(\"/ipfs\", \"/ipns\"),\n\t\tipfs_corehttp.VersionOption(),\n\t\tipfs_corehttp.CheckVersionOption(),\n\t\tipfs_corehttp.CommandsOption(im.commandCtx),\n\t)\n\n\treturn ipfs_corehttp.Serve(im.IpfsNode, l, opts...)\n}\n\nfunc NewNode(ctx context.Context, cfg *IpfsConfig) (*IpfsMobile, error) {\n\tif err := cfg.fillDefault(); err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid configuration: %w\", err)\n\t}\n\n\t// build config\n\tbuildcfg := &ipfs_core.BuildCfg{\n\t\tOnline:                      true,\n\t\tPermanent:                   false,\n\t\tDisableEncryptedConnections: false,\n\t\tRepo:                        cfg.RepoMobile,\n\t\tHost:                        NewHostConfigOption(cfg.HostOption, cfg.HostConfig),\n\t\tRouting:                     NewRoutingConfigOption(cfg.RoutingOption, cfg.RoutingConfig),\n\t\tExtraOpts:                   cfg.ExtraOpts,\n\t}\n\tos.Setenv(\"IPFS_PATH\", cfg.RepoMobile.Path)\n\n\t// create ipfs node\n\tinode, err := ipfs_core.NewNode(ctx, buildcfg)\n\tif err != nil {\n\t\t// unlockRepo(repoPath)\n\t\treturn nil, fmt.Errorf(\"failed to init ipfs node: %s\", err)\n\t}\n\n\t// @TODO: no sure about how to init this, must be another way\n\tcctx := ipfs_oldcmds.Context{\n\t\tConfigRoot: cfg.RepoMobile.Path,\n\t\tReqLog:     &ipfs_oldcmds.ReqLog{},\n\t\tConstructNode: func() (*ipfs_core.IpfsNode, error) {\n\t\t\treturn inode, nil\n\t\t},\n\t}\n\n\treturn &IpfsMobile{\n\t\tcommandCtx: cctx,\n\t\tIpfsNode:   inode,\n\t\tRepo:       cfg.RepoMobile,\n\t}, nil\n}\n"
  },
  {
    "path": "pkg/ipfsutil/mobile/repo.go",
    "content": "package node\n\nimport (\n\tipfs_config \"github.com/ipfs/kubo/config\"\n\tipfs_repo \"github.com/ipfs/kubo/repo\"\n)\n\nvar _ ipfs_repo.Repo = (*RepoMobile)(nil)\n\ntype RepoConfigPatch func(cfg *ipfs_config.Config) (err error)\n\ntype RepoMobile struct {\n\tipfs_repo.Repo\n\n\tPath string\n}\n\nfunc NewRepoMobile(path string, repo ipfs_repo.Repo) *RepoMobile {\n\treturn &RepoMobile{\n\t\tRepo: repo,\n\t\tPath: path,\n\t}\n}\n\nfunc (mr *RepoMobile) ApplyPatchs(patchs ...RepoConfigPatch) error {\n\tcfg, err := mr.Config()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif err := ChainIpfsConfigPatch(patchs...)(cfg); err != nil {\n\t\treturn err\n\t}\n\n\treturn mr.SetConfig(cfg)\n}\n\nfunc ChainIpfsConfigPatch(patchs ...RepoConfigPatch) RepoConfigPatch {\n\treturn func(cfg *ipfs_config.Config) (err error) {\n\t\tfor _, patch := range patchs {\n\t\t\tif patch == nil {\n\t\t\t\tcontinue // skip empty patch\n\t\t\t}\n\n\t\t\tif err = patch(cfg); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "pkg/ipfsutil/mobile/routing.go",
    "content": "package node\n\nimport (\n\t\"fmt\"\n\n\tipfs_p2p \"github.com/ipfs/kubo/core/node/libp2p\"\n\tp2p_host \"github.com/libp2p/go-libp2p/core/host\"\n\tp2p_routing \"github.com/libp2p/go-libp2p/core/routing\"\n)\n\ntype RoutingConfigFunc func(p2p_host.Host, p2p_routing.Routing) error\n\ntype RoutingConfig struct {\n\tConfigFunc RoutingConfigFunc\n}\n\nfunc NewRoutingConfigOption(ro ipfs_p2p.RoutingOption, rc *RoutingConfig) ipfs_p2p.RoutingOption {\n\treturn func(args ipfs_p2p.RoutingOptionArgs) (p2p_routing.Routing, error) {\n\t\trouting, err := ro(args)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tif rc.ConfigFunc != nil {\n\t\t\tif err := rc.ConfigFunc(args.Host, routing); err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"failed to config routing: %w\", err)\n\t\t\t}\n\t\t}\n\n\t\treturn routing, nil\n\t}\n}\n"
  },
  {
    "path": "pkg/ipfsutil/mobile.go",
    "content": "package ipfsutil\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\tipfs_config \"github.com/ipfs/kubo/config\"\n\tipfs_p2p \"github.com/ipfs/kubo/core/node/libp2p\"\n\tp2p \"github.com/libp2p/go-libp2p\"\n\tdht \"github.com/libp2p/go-libp2p-kad-dht\"\n\t\"github.com/libp2p/go-libp2p-kad-dht/dual\"\n\thost \"github.com/libp2p/go-libp2p/core/host\"\n\t\"github.com/libp2p/go-libp2p/core/peer\"\n\tp2p_routing \"github.com/libp2p/go-libp2p/core/routing\"\n\tquict \"github.com/libp2p/go-libp2p/p2p/transport/quic\"\n\t\"github.com/libp2p/go-libp2p/p2p/transport/quicreuse\"\n\ttcpt \"github.com/libp2p/go-libp2p/p2p/transport/tcp\"\n\t\"go.uber.org/zap\"\n\n\tipfs_mobile \"berty.tech/weshnet/v2/pkg/ipfsutil/mobile\"\n)\n\ntype DHTNetworkMode int\n\nconst (\n\tDHTNetworkLan DHTNetworkMode = iota\n\tDHTNetworkWan\n\tDHTNetworkDual\n)\n\ntype Config func(cfg *ipfs_config.Config) ([]p2p.Option, error)\n\ntype MobileOptions struct {\n\tLogger          *zap.Logger\n\tIpfsConfigPatch Config\n\t// P2PStaticRelays and PeerStorePeers are only used if IpfsConfigPatch is nil\n\tP2PStaticRelays []string\n\tPeerStorePeers  []string\n\n\tHostOption    ipfs_p2p.HostOption\n\tRoutingOption ipfs_p2p.RoutingOption\n\n\tHostConfigFunc    ipfs_mobile.HostConfigFunc\n\tRoutingConfigFunc ipfs_mobile.RoutingConfigFunc\n\n\tExtraOpts map[string]bool\n}\n\nfunc (o *MobileOptions) fillDefault() {\n\tif o.Logger == nil {\n\t\to.Logger = zap.NewNop()\n\t}\n\n\tif o.HostOption == nil {\n\t\to.HostOption = ipfs_p2p.DefaultHostOption\n\t}\n\n\tif o.RoutingOption == nil {\n\t\to.RoutingOption = CustomRoutingOption(dht.ModeClient, DHTNetworkDual, dht.Concurrency(2))\n\t}\n\n\tif o.IpfsConfigPatch == nil {\n\t\to.IpfsConfigPatch = o.defaultIpfsConfigPatch\n\n\t\t// P2PStaticRelays and PeerStorePeers are only used by defaultIpfsConfigPatch\n\t\tif o.P2PStaticRelays == nil {\n\t\t\to.P2PStaticRelays = []string{DefaultP2PStaticRelay}\n\t\t}\n\t\tif o.PeerStorePeers == nil {\n\t\t\to.PeerStorePeers = []string{DefaultP2PRdvpMaddr}\n\t\t}\n\t}\n\n\t// apply default extras\n\tif o.ExtraOpts == nil {\n\t\to.ExtraOpts = make(map[string]bool)\n\t}\n\n\t//  if not set, disable pubsub by default to avoid collision\n\tif _, ok := o.ExtraOpts[\"pubsub\"]; !ok {\n\t\to.ExtraOpts[\"pubsub\"] = false\n\t}\n}\n\nfunc NewIPFSMobile(ctx context.Context, repo *ipfs_mobile.RepoMobile, opts *MobileOptions) (*ipfs_mobile.IpfsMobile, error) {\n\topts.fillDefault()\n\n\tvar p2popts []p2p.Option\n\n\terr := repo.ApplyPatchs(func(cfg *ipfs_config.Config) error {\n\t\tvar err error\n\t\tif p2popts, err = opts.IpfsConfigPatch(cfg); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\treturn nil\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// check that p2p opt is set\n\tif p2popts == nil {\n\t\treturn nil, fmt.Errorf(\"unable p2p option: cannot be nil\")\n\t}\n\n\t// configure host\n\thostconfig := &ipfs_mobile.HostConfig{\n\t\t// called after host init\n\t\tConfigFunc: opts.HostConfigFunc,\n\n\t\t// p2p options\n\t\tOptions: p2popts,\n\t}\n\n\t// configure routing\n\troutingconfig := &ipfs_mobile.RoutingConfig{\n\t\t// called after host init\n\t\tConfigFunc: opts.RoutingConfigFunc,\n\t}\n\n\t// configure ipfs mobile\n\tipfsconfig := ipfs_mobile.IpfsConfig{\n\t\tHostConfig:    hostconfig,\n\t\tRoutingConfig: routingconfig,\n\t\tRepoMobile:    repo,\n\t\tExtraOpts:     opts.ExtraOpts,\n\t\tHostOption:    opts.HostOption,\n\t\tRoutingOption: opts.RoutingOption,\n\t}\n\n\treturn ipfs_mobile.NewNode(ctx, &ipfsconfig)\n}\n\nfunc CustomRoutingOption(mode dht.ModeOpt, net DHTNetworkMode, opts ...dht.Option) func(args ipfs_p2p.RoutingOptionArgs) (p2p_routing.Routing, error) {\n\treturn func(args ipfs_p2p.RoutingOptionArgs) (p2p_routing.Routing, error) {\n\t\topts = append(opts,\n\t\t\tdht.Mode(mode),\n\t\t\tdht.Datastore(args.Datastore),\n\t\t\tdht.Validator(args.Validator),\n\t\t\tdht.BootstrapPeers(args.BootstrapPeers...),\n\t\t)\n\n\t\treturn newDualDHT(args.Ctx, args.Host, net, opts...)\n\t}\n}\n\nfunc (o *MobileOptions) defaultIpfsConfigPatch(cfg *ipfs_config.Config) ([]p2p.Option, error) {\n\t// Imitate berty setupIPFSConfig\n\t// https://github.com/berty/berty/blob/5a8b9cb8524c1287ab2533a9e186ac8bde7f2b57/go/internal/initutil/ipfs.go#L474C19-L474C34\n\tp2popts := []p2p.Option{}\n\n\t// make sure relay is enabled\n\tcfg.Swarm.RelayClient.Enabled = ipfs_config.True\n\tcfg.Swarm.Transports.Network.Relay = ipfs_config.True\n\n\t// add static relay\n\tpis, err := ParseAndResolveMaddrs(context.TODO(), o.Logger, o.P2PStaticRelays)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif len(pis) > 0 {\n\t\tpeers := make([]peer.AddrInfo, len(pis))\n\t\tfor i, p := range pis {\n\t\t\tpeers[i] = *p\n\t\t}\n\n\t\tp2popts = append(p2popts, p2p.EnableAutoRelayWithStaticRelays(peers))\n\t}\n\n\t// prefill peerstore with known rdvp servers\n\tpeers, err := ParseAndResolveMaddrs(context.TODO(), o.Logger, o.PeerStorePeers)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, p := range peers {\n\t\tcfg.Peering.Peers = append(cfg.Peering.Peers, *p)\n\t}\n\n\t// @NOTE(gfanton): disable quic transport so we can init a custom transport\n\t// with reusport disabled\n\tcfg.Swarm.Transports.Network.QUIC = ipfs_config.False\n\tp2popts = append(p2popts, p2p.Transport(quict.NewTransport), p2p.QUICReuse(quicreuse.NewConnManager, quicreuse.DisableReuseport()))\n\n\t// @NOTE(gfanton): disable tcp transport so we can init a custom transport\n\t// with reusport disabled\n\tcfg.Swarm.Transports.Network.TCP = ipfs_config.False\n\tp2popts = append(p2popts, p2p.Transport(tcpt.NewTCPTransport,\n\t\ttcpt.DisableReuseport(),\n\t))\n\n\treturn p2popts, nil\n}\n\nconst (\n\t// from dual package dht\n\tmaxPrefixCountPerCpl = 2\n\tmaxPrefixCount       = 3\n)\n\nfunc newDualDHT(ctx context.Context, h host.Host, net DHTNetworkMode, options ...dht.Option) (p2p_routing.Routing, error) {\n\tswitch net {\n\tcase DHTNetworkWan:\n\t\toptions = append(options,\n\t\t\tdht.QueryFilter(dht.PublicQueryFilter),\n\t\t\tdht.RoutingTableFilter(dht.PublicRoutingTableFilter),\n\t\t\tdht.RoutingTablePeerDiversityFilter(dht.NewRTPeerDiversityFilter(h, maxPrefixCountPerCpl, maxPrefixCount)),\n\t\t)\n\n\t\treturn dht.New(ctx, h, options...)\n\tcase DHTNetworkLan:\n\t\toptions = append(options,\n\t\t\tdht.ProtocolExtension(dual.LanExtension),\n\t\t\tdht.QueryFilter(dht.PrivateQueryFilter),\n\t\t\tdht.RoutingTableFilter(dht.PrivateRoutingTableFilter),\n\t\t)\n\n\t\treturn dht.New(ctx, h, options...)\n\tdefault: // dual\n\t\treturn dual.New(ctx, h, dual.DHTOption(options...))\n\t}\n}\n"
  },
  {
    "path": "pkg/ipfsutil/peering.go",
    "content": "// mostly from: https://github.com/ipfs/kubo/blob/master/peering/peering.go\n\npackage ipfsutil\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/libp2p/go-libp2p/core/host\"\n\t\"github.com/libp2p/go-libp2p/core/network\"\n\t\"github.com/libp2p/go-libp2p/core/peer\"\n\tbackoff \"github.com/libp2p/go-libp2p/p2p/discovery/backoff\"\n\t\"github.com/multiformats/go-multiaddr\"\n\t\"go.uber.org/zap\"\n\n\t\"berty.tech/weshnet/v2/pkg/logutil\"\n)\n\n// Seed the random number generator.\n//\n// We don't need good randomness, but we do need randomness.\nconst (\n\tconnmgrTag = \"berty-peering\"\n\n\t// MaximumReconnectingDelay define the maximum time a peer is able to\n\t// reconnect, if reached peer will be remove from the store\n\tMaximumReconnectingDelay = time.Second * 30\n)\n\ntype state int\n\nconst (\n\tstateInit state = iota\n\tstateRunning\n\tstateStopped\n)\n\n// PeeringService maintains connections to specified peers, reconnecting on\n// disconnect with a back-off.\ntype PeeringService struct {\n\thost           host.Host\n\tlogger         *zap.Logger\n\tbackoffFactory backoff.BackoffFactory\n\n\tmuPeers sync.RWMutex\n\tpeers   map[peer.ID]*peerHandler\n\tstate   state\n}\n\n// NewPeeringService constructs a new peering service. Peers can be added and\n// removed immediately, but connections won't be formed until `Start` is called.\nfunc NewPeeringService(logger *zap.Logger, host host.Host, fact backoff.BackoffFactory) *PeeringService {\n\treturn &PeeringService{\n\t\tbackoffFactory: fact,\n\t\tlogger:         logger,\n\t\thost:           host,\n\t\tpeers:          make(map[peer.ID]*peerHandler),\n\t}\n}\n\n// Start starts the peering service, connecting and maintaining connections to\n// all registered peers. It returns an error if the service has already been\n// stopped.\nfunc (ps *PeeringService) Start() error {\n\tps.muPeers.Lock()\n\tdefer ps.muPeers.Unlock()\n\n\tswitch ps.state {\n\tcase stateInit:\n\t\tps.logger.Info(\"starting peering service\")\n\tcase stateRunning:\n\t\treturn nil\n\tcase stateStopped:\n\t\treturn errors.New(\"peering service already stopped\")\n\t}\n\tps.host.Network().Notify((*netNotifee)(ps))\n\tps.state = stateRunning\n\tfor _, handler := range ps.peers {\n\t\tgo handler.startIfDisconnected()\n\t}\n\treturn nil\n}\n\n// Stop stops the peering service.\nfunc (ps *PeeringService) Stop() error {\n\tps.host.Network().StopNotify((*netNotifee)(ps))\n\n\tps.muPeers.Lock()\n\tdefer ps.muPeers.Unlock()\n\n\tswitch ps.state {\n\tcase stateInit, stateRunning:\n\t\tps.logger.Info(\"stopping peering service\")\n\t\tfor _, handler := range ps.peers {\n\t\t\thandler.stop()\n\t\t}\n\t\tps.state = stateStopped\n\t}\n\treturn nil\n}\n\n// AddPeer adds a peer to the peering service. This function may be safely\n// called at any time: before the service is started, while running, or after it\n// stops.\n//\n// Add peer may also be called multiple times for the same peer. The new\n// addresses will replace the old.\nfunc (ps *PeeringService) AddPeer(info peer.AddrInfo) {\n\tps.muPeers.Lock()\n\thandler, ok := ps.peers[info.ID]\n\tps.muPeers.Unlock()\n\n\tif ok {\n\t\tps.logger.Info(\"updating addresses\", logutil.PrivateStringer(\"peer\", info.ID), zap.Any(\"addrs\", info.Addrs))\n\t\thandler.setAddrs(info.Addrs)\n\t} else {\n\t\tps.logger.Info(\"peer added\", logutil.PrivateStringer(\"peer\", info.ID), zap.Any(\"addrs\", info.Addrs))\n\t\tps.host.ConnManager().Protect(info.ID, connmgrTag)\n\t\thandler = &peerHandler{\n\t\t\tlogger:       ps.logger,\n\t\t\thost:         ps.host,\n\t\t\tpeer:         info.ID,\n\t\t\taddrs:        info.Addrs,\n\t\t\tbackoffStrat: ps.backoffFactory(),\n\t\t}\n\n\t\thandler.ctx, handler.cancel = context.WithCancel(context.Background())\n\t\tps.peers[info.ID] = handler\n\t\tswitch ps.state {\n\t\tcase stateRunning:\n\t\t\tgo handler.startIfDisconnected()\n\t\tcase stateStopped:\n\t\t\t// We still construct everything in this state because\n\t\t\t// it's easier to reason about. But we should still free\n\t\t\t// resources.\n\t\t\thandler.cancel()\n\t\t}\n\t}\n}\n\n// ListPeers lists peers in the peering service.\nfunc (ps *PeeringService) ListPeers() []peer.AddrInfo {\n\tps.muPeers.RLock()\n\tdefer ps.muPeers.RUnlock()\n\n\tout := make([]peer.AddrInfo, 0, len(ps.peers))\n\tfor id, addrs := range ps.peers {\n\t\tai := peer.AddrInfo{ID: id}\n\t\tai.Addrs = append(ai.Addrs, addrs.addrs...)\n\t\tout = append(out, ai)\n\t}\n\treturn out\n}\n\n// RemovePeer removes a peer from the peering service. This function may be\n// safely called at any time: before the service is started, while running, or\n// after it stops.\nfunc (ps *PeeringService) RemovePeer(id peer.ID) {\n\tps.muPeers.Lock()\n\tdefer ps.muPeers.Unlock()\n\n\tif handler, ok := ps.peers[id]; ok {\n\t\tps.logger.Info(\"peer removed\", logutil.PrivateStringer(\"peer\", id))\n\t\tps.host.ConnManager().Unprotect(id, connmgrTag)\n\n\t\thandler.stop()\n\t\tdelete(ps.peers, id)\n\t}\n}\n\ntype netNotifee PeeringService\n\nfunc (nn *netNotifee) Connected(_ network.Network, c network.Conn) {\n\tps := (*PeeringService)(nn)\n\n\tp := c.RemotePeer()\n\tps.muPeers.RLock()\n\tdefer ps.muPeers.RUnlock()\n\n\tif handler, ok := ps.peers[p]; ok {\n\t\t// use a goroutine to avoid blocking events.\n\t\tgo handler.stopIfConnected()\n\t}\n}\n\nfunc (nn *netNotifee) Disconnected(_ network.Network, c network.Conn) {\n\tps := (*PeeringService)(nn)\n\n\tp := c.RemotePeer()\n\tps.muPeers.RLock()\n\tdefer ps.muPeers.RUnlock()\n\n\tif handler, ok := ps.peers[p]; ok {\n\t\t// use a goroutine to avoid blocking events.\n\t\tgo handler.startIfDisconnected()\n\t}\n}\nfunc (nn *netNotifee) OpenedStream(network.Network, network.Stream)     {}\nfunc (nn *netNotifee) ClosedStream(network.Network, network.Stream)     {}\nfunc (nn *netNotifee) Listen(network.Network, multiaddr.Multiaddr)      {}\nfunc (nn *netNotifee) ListenClose(network.Network, multiaddr.Multiaddr) {}\n\n// peerHandler keeps track of all state related to a specific \"peering\" peer.\ntype peerHandler struct {\n\tpeer   peer.ID\n\thost   host.Host\n\tctx    context.Context\n\tcancel context.CancelFunc\n\tlogger *zap.Logger\n\n\taddrs          []multiaddr.Multiaddr\n\tbackoffStrat   backoff.BackoffStrategy\n\treconnectTimer *time.Timer\n\n\tmuHandler sync.Mutex\n}\n\n// setAddrs sets the addresses for this peer.\nfunc (ph *peerHandler) setAddrs(addrs []multiaddr.Multiaddr) {\n\t// Not strictly necessary, but it helps to not trust the calling code.\n\taddrCopy := make([]multiaddr.Multiaddr, len(addrs))\n\tcopy(addrCopy, addrs)\n\n\tph.muHandler.Lock()\n\tph.addrs = addrCopy\n\tph.muHandler.Unlock()\n}\n\n// getAddrs returns a shared slice of addresses for this peer. Do not modify.\nfunc (ph *peerHandler) getAddrs() []multiaddr.Multiaddr {\n\tph.muHandler.Lock()\n\tdefer ph.muHandler.Unlock()\n\treturn ph.addrs\n}\n\n// stop permanently stops the peer handler.\nfunc (ph *peerHandler) stop() {\n\tph.cancel()\n\n\tph.muHandler.Lock()\n\n\tif ph.reconnectTimer != nil {\n\t\tph.reconnectTimer.Stop()\n\t\tph.reconnectTimer = nil\n\t}\n\n\tph.muHandler.Unlock()\n}\n\nfunc (ph *peerHandler) nextBackoff() time.Duration {\n\treturn ph.backoffStrat.Delay()\n}\n\nfunc (ph *peerHandler) reconnect() {\n\t// Try connecting\n\taddrs := ph.getAddrs()\n\tph.logger.Debug(\"reconnecting\", logutil.PrivateStringer(\"peer\", ph.peer), zap.Any(\"addrs\", addrs))\n\n\terr := ph.host.Connect(ph.ctx, peer.AddrInfo{ID: ph.peer, Addrs: addrs})\n\tif err != nil {\n\t\tdelay := ph.nextBackoff()\n\t\tif delay > MaximumReconnectingDelay {\n\t\t\tph.logger.Debug(\"peer unavailable\", logutil.PrivateStringer(\"peer\", ph.peer))\n\t\t\tph.stop()\n\t\t\treturn\n\t\t}\n\n\t\tph.logger.Debug(\"failed to reconnect\", zap.Duration(\"next_try\", delay), logutil.PrivateStringer(\"peer\", ph.peer), zap.Error(err))\n\t\t// Ok, we failed. Extend the timeout.\n\t\tph.muHandler.Lock()\n\t\tif ph.reconnectTimer != nil {\n\t\t\t// Only counts if the reconnectTimer still exists. If not, a\n\t\t\t// connection _was_ somehow established.\n\t\t\tph.reconnectTimer.Reset(delay)\n\t\t}\n\t\t// Otherwise, someone else has stopped us so we can assume that\n\t\t// we're either connected or someone else will start us.\n\t\tph.muHandler.Unlock()\n\t}\n\n\t// Always call this. We could have connected since we processed the\n\t// error.\n\tph.stopIfConnected()\n}\n\nfunc (ph *peerHandler) stopIfConnected() {\n\tph.muHandler.Lock()\n\tdefer ph.muHandler.Unlock()\n\n\tif ph.reconnectTimer != nil && ph.host.Network().Connectedness(ph.peer) == network.Connected {\n\t\tph.logger.Debug(\"successfully reconnected\", logutil.PrivateStringer(\"peer\", ph.peer))\n\n\t\t// stop reconnect timer\n\t\tph.reconnectTimer.Stop()\n\t\tph.reconnectTimer = nil\n\t}\n}\n\n// startIfDisconnected is the inverse of stopIfConnected.\nfunc (ph *peerHandler) startIfDisconnected() {\n\tph.muHandler.Lock()\n\tdefer ph.muHandler.Unlock()\n\n\tif ph.reconnectTimer == nil && ph.host.Network().Connectedness(ph.peer) != network.Connected {\n\t\t// reset backoff\n\t\tph.backoffStrat.Reset()\n\n\t\tdelay := ph.nextBackoff()\n\t\tph.logger.Debug(\"disconnected from peer, waiting for reconnection\", logutil.PrivateStringer(\"peer\", ph.peer), zap.Duration(\"delay\", delay))\n\n\t\t// Always start with a short timeout so we can stagger things a bit.\n\t\tph.reconnectTimer = time.AfterFunc(delay, ph.reconnect)\n\t}\n}\n"
  },
  {
    "path": "pkg/ipfsutil/pubsub_adaptater.go",
    "content": "package ipfsutil\n\nimport (\n\tcoreiface \"github.com/ipfs/kubo/core/coreiface\"\n)\n\ntype pubsubCoreAPIAdapter struct {\n\tcoreiface.PubSubAPI\n\n\tcoreiface.CoreAPI\n}\n\nfunc (ps *pubsubCoreAPIAdapter) PubSub() coreiface.PubSubAPI {\n\treturn ps.PubSubAPI\n}\n\nfunc InjectPubSubAPI(api coreiface.CoreAPI, ps coreiface.PubSubAPI) coreiface.CoreAPI {\n\treturn &pubsubCoreAPIAdapter{\n\t\tPubSubAPI: ps,\n\t\tCoreAPI:   api,\n\t}\n}\n\ntype pubsubExtendedCoreAPIAdapter struct {\n\tcoreiface.PubSubAPI\n\n\tExtendedCoreAPI\n}\n\nfunc (ps *pubsubExtendedCoreAPIAdapter) PubSub() coreiface.PubSubAPI {\n\treturn ps.PubSubAPI\n}\n\nfunc InjectPubSubCoreAPIExtendedAdapter(exapi ExtendedCoreAPI, ps coreiface.PubSubAPI) ExtendedCoreAPI {\n\treturn &pubsubExtendedCoreAPIAdapter{\n\t\tPubSubAPI:       ps,\n\t\tExtendedCoreAPI: exapi,\n\t}\n}\n"
  },
  {
    "path": "pkg/ipfsutil/pubsub_api.go",
    "content": "package ipfsutil\n\nimport (\n\t\"context\"\n\t\"sync\"\n\n\tcoreiface \"github.com/ipfs/kubo/core/coreiface\"\n\tcoreiface_options \"github.com/ipfs/kubo/core/coreiface/options\"\n\tp2p_pubsub \"github.com/libp2p/go-libp2p-pubsub\"\n\tp2p_peer \"github.com/libp2p/go-libp2p/core/peer\"\n\t\"go.uber.org/zap\"\n\n\t\"berty.tech/weshnet/v2/pkg/logutil\"\n)\n\ntype PubSubAPI struct {\n\t*p2p_pubsub.PubSub\n\tlogger *zap.Logger\n\n\tmuTopics sync.RWMutex\n\ttopics   map[string]*p2p_pubsub.Topic\n}\n\nfunc NewPubSubAPI(_ context.Context, logger *zap.Logger, ps *p2p_pubsub.PubSub) coreiface.PubSubAPI {\n\treturn &PubSubAPI{\n\t\tPubSub: ps,\n\n\t\tlogger: logger,\n\t\ttopics: make(map[string]*p2p_pubsub.Topic),\n\t}\n}\n\nfunc (ps *PubSubAPI) topicJoin(topic string, opts ...p2p_pubsub.TopicOpt) (*p2p_pubsub.Topic, error) {\n\tps.muTopics.Lock()\n\tdefer ps.muTopics.Unlock()\n\n\tvar err error\n\n\tt, ok := ps.topics[topic]\n\tif ok {\n\t\treturn t, nil\n\t}\n\n\tif t, err = ps.PubSub.Join(topic, opts...); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif _, err = t.Relay(); err != nil {\n\t\tt.Close()\n\t\treturn nil, err\n\t}\n\n\tps.topics[topic] = t\n\treturn t, nil\n}\n\n// func (ps *PubSubAPI) topicLeave(topic string) (err error) {\n// \tps.muTopics.Lock()\n// \tif t, ok := ps.topics[topic]; ok {\n// \t\terr = t.Close()\n// \t\tdelete(ps.topics, topic)\n// \t}\n// \tps.muTopics.Unlock()\n// \treturn\n// }\n\n// Ls lists subscribed topics by name\nfunc (ps *PubSubAPI) Ls(context.Context) ([]string, error) {\n\treturn ps.PubSub.GetTopics(), nil\n}\n\n// Peers list peers we are currently pubsubbing with\nfunc (ps *PubSubAPI) Peers(_ context.Context, opts ...coreiface_options.PubSubPeersOption) ([]p2p_peer.ID, error) {\n\ts, err := coreiface_options.PubSubPeersOptions(opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn ps.PubSub.ListPeers(s.Topic), nil\n}\n\nvar minTopicSize = p2p_pubsub.WithReadiness(p2p_pubsub.MinTopicSize(1))\n\n// Publish a message to a given pubsub topic\nfunc (ps *PubSubAPI) Publish(ctx context.Context, topic string, msg []byte) error {\n\tt, err := ps.topicJoin(topic)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn t.Publish(ctx, msg, minTopicSize)\n}\n\n// Subscribe to messages on a given topic\nfunc (ps *PubSubAPI) Subscribe(_ context.Context, topic string, _ ...coreiface_options.PubSubSubscribeOption) (coreiface.PubSubSubscription, error) {\n\tt, err := ps.topicJoin(topic)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tps.logger.Debug(\"subscribing\", logutil.PrivateString(\"topic\", topic))\n\tsub, err := t.Subscribe()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &pubsubSubscriptionAPI{ps.logger, sub}, nil\n}\n\n// PubSubSubscription is an active PubSub subscription\ntype pubsubSubscriptionAPI struct {\n\tlogger *zap.Logger\n\t*p2p_pubsub.Subscription\n}\n\n// io.Closer\nfunc (pss *pubsubSubscriptionAPI) Close() (_ error) {\n\tpss.Subscription.Cancel()\n\treturn\n}\n\n// Next return the next incoming message\nfunc (pss *pubsubSubscriptionAPI) Next(ctx context.Context) (coreiface.PubSubMessage, error) {\n\tm, err := pss.Subscription.Next(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &pubsubMessageAPI{m}, nil\n}\n\n// PubSubMessage is a single PubSub message\ntype pubsubMessageAPI struct {\n\t*p2p_pubsub.Message\n}\n\n// From returns id of a peer from which the message has arrived\nfunc (psm *pubsubMessageAPI) From() p2p_peer.ID {\n\treturn psm.Message.GetFrom()\n}\n\n// Data returns the message body\nfunc (psm *pubsubMessageAPI) Data() []byte {\n\treturn psm.Message.GetData()\n}\n\n// Seq returns message identifier\nfunc (psm *pubsubMessageAPI) Seq() []byte {\n\treturn psm.Message.GetSeqno()\n}\n\n// // Topics returns list of topics this message was set to\nfunc (psm *pubsubMessageAPI) Topics() []string {\n\tif psm.Message.Topic == nil {\n\t\treturn nil\n\t}\n\treturn []string{*psm.Message.Topic}\n}\n"
  },
  {
    "path": "pkg/ipfsutil/pubsub_monitor.go",
    "content": "package ipfsutil\n\nimport (\n\t\"sync\"\n\n\tps \"github.com/libp2p/go-libp2p-pubsub\"\n\tps_pb \"github.com/libp2p/go-libp2p-pubsub/pb\"\n\t\"github.com/libp2p/go-libp2p/core/event\"\n\thost \"github.com/libp2p/go-libp2p/core/host\"\n\t\"github.com/libp2p/go-libp2p/core/peer\"\n\t\"go.uber.org/zap\"\n\n\t\"berty.tech/weshnet/v2/pkg/logutil\"\n)\n\n// PubsubMonitor is an EventTracer\nvar _ ps.EventTracer = (*PubsubMonitor)(nil)\n\ntype EventMonitor int\n\nconst (\n\tTypeEventMonitorPeerUnknown EventMonitor = iota\n\tTypeEventMonitorPeerJoined\n\tTypeEventMonitorPeerLeft\n)\n\ntype EventTracer interface {\n\tEventTracerOption() ps.Option\n}\n\ntype EvtPubSubTopic struct {\n\tEventType EventMonitor\n\tTopic     string\n\tPeerID    peer.ID\n}\n\ntype PubsubMonitor struct {\n\tlogger  *zap.Logger\n\th       host.Host\n\tps      *ps.PubSub\n\temitter event.Emitter\n\n\tmuPeersTopics sync.Mutex\n\tpeersTopics   map[peer.ID][]string\n}\n\nfunc NewPubsubMonitor(l *zap.Logger, h host.Host) (EventTracer, error) {\n\temitter, err := h.EventBus().Emitter(new(EvtPubSubTopic))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &PubsubMonitor{\n\t\th:           h,\n\t\tlogger:      l,\n\t\temitter:     emitter,\n\t\tpeersTopics: make(map[peer.ID][]string),\n\t}, nil\n}\n\nfunc (pt *PubsubMonitor) EventTracerOption() ps.Option {\n\treturn func(p *ps.PubSub) error {\n\t\tpt.ps = p\n\t\treturn ps.WithEventTracer(pt)(p)\n\t}\n}\n\nfunc (pt *PubsubMonitor) Trace(e *ps_pb.TraceEvent) {\n\tswitch e.GetType() {\n\tcase ps_pb.TraceEvent_JOIN:\n\t\ttopic := e.GetJoin().GetTopic()\n\t\tpeer := pt.h.ID()\n\t\tpt.Emit(&EvtPubSubTopic{\n\t\t\tEventType: TypeEventMonitorPeerJoined,\n\t\t\tTopic:     topic,\n\t\t\tPeerID:    peer,\n\t\t})\n\n\tcase ps_pb.TraceEvent_LEAVE:\n\t\ttopic := e.GetLeave().GetTopic()\n\t\tpeer := pt.h.ID()\n\t\tpt.Emit(&EvtPubSubTopic{\n\t\t\tEventType: TypeEventMonitorPeerLeft,\n\t\t\tTopic:     topic,\n\t\t\tPeerID:    peer,\n\t\t})\n\n\tcase ps_pb.TraceEvent_REMOVE_PEER:\n\t\tpeerid, err := peer.IDFromBytes(e.GetRemovePeer().GetPeerID())\n\t\tif err != nil {\n\t\t\tpt.logger.Warn(\"unable to parse peerid\",\n\t\t\t\tzap.String(\"type\", e.GetType().String()),\n\t\t\t\tlogutil.PrivateString(\"topic\", e.GetGraft().GetTopic()))\n\t\t\treturn\n\t\t}\n\n\t\ttopics := pt.popTopicFromPeer(peerid)\n\t\tfor _, topic := range topics {\n\t\t\tpt.Emit(&EvtPubSubTopic{\n\t\t\t\tEventType: TypeEventMonitorPeerLeft,\n\t\t\t\tTopic:     topic,\n\t\t\t\tPeerID:    peerid,\n\t\t\t})\n\t\t}\n\n\tcase ps_pb.TraceEvent_GRAFT:\n\t\ttopic := e.GetGraft().GetTopic()\n\t\tpeerid, err := peer.IDFromBytes(e.GetGraft().GetPeerID())\n\t\tif err != nil {\n\t\t\tpt.logger.Warn(\"unable to parse peerid\",\n\t\t\t\tzap.String(\"type\", e.GetType().String()),\n\t\t\t\tlogutil.PrivateString(\"topic\", e.GetGraft().GetTopic()))\n\t\t\treturn\n\t\t}\n\n\t\tpt.addTopicToPeer(peerid, topic)\n\t\tpt.Emit(&EvtPubSubTopic{\n\t\t\tEventType: TypeEventMonitorPeerJoined,\n\t\t\tTopic:     topic,\n\t\t\tPeerID:    peerid,\n\t\t})\n\n\tcase ps_pb.TraceEvent_PRUNE:\n\t\t// @FIXME(gfanton): send this info as well\n\t}\n}\n\nfunc (pt *PubsubMonitor) Emit(e *EvtPubSubTopic) {\n\tif err := pt.emitter.Emit(*e); err != nil {\n\t\tpt.logger.Warn(\"unable to emit pubsub event\")\n\t}\n}\n\nfunc (pt *PubsubMonitor) popTopicFromPeer(p peer.ID) []string {\n\tpt.muPeersTopics.Lock()\n\tdefer pt.muPeersTopics.Unlock()\n\n\tif topics, ok := pt.peersTopics[p]; ok {\n\t\tdelete(pt.peersTopics, p)\n\t\treturn topics\n\t}\n\n\treturn []string{}\n}\n\nfunc (pt *PubsubMonitor) addTopicToPeer(p peer.ID, ns string) {\n\tpt.muPeersTopics.Lock()\n\n\ttopics, ok := pt.peersTopics[p]\n\tif !ok {\n\t\ttopics = make([]string, 0)\n\t}\n\tpt.peersTopics[p] = append(topics, ns)\n\n\tpt.muPeersTopics.Unlock()\n}\n"
  },
  {
    "path": "pkg/ipfsutil/repo.go",
    "content": "package ipfsutil\n\nimport (\n\tcrand \"crypto/rand\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"path/filepath\"\n\t\"time\"\n\n\tipfs_ds \"github.com/ipfs/go-datastore\"\n\tipfs_cfg \"github.com/ipfs/kubo/config\"\n\tipfs_loader \"github.com/ipfs/kubo/plugin/loader\"\n\tipfs_repo \"github.com/ipfs/kubo/repo\"\n\tipfs_fsrepo \"github.com/ipfs/kubo/repo/fsrepo\"\n\tp2p_ci \"github.com/libp2p/go-libp2p/core/crypto\"\n\tp2p_peer \"github.com/libp2p/go-libp2p/core/peer\"\n\t\"github.com/pkg/errors\"\n\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n)\n\n// defaultConnMgrHighWater is the default value for the connection managers\n// 'high water' mark\nconst defaultConnMgrHighWater = 200\n\n// defaultConnMgrLowWater is the default value for the connection managers 'low\n// water' mark\nconst defaultConnMgrLowWater = 150\n\n// defaultConnMgrGracePeriod is the default value for the connection managers\n// grace period\nconst defaultConnMgrGracePeriod = time.Second * 20\n\n// @NOTE(gfanton): this will be removed with gomobile-ipfs\nvar plugins *ipfs_loader.PluginLoader\n\nfunc CreateMockedRepo(dstore ipfs_ds.Batching) (ipfs_repo.Repo, error) {\n\tc, err := CreateBaseConfig()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &ipfs_repo.Mock{\n\t\tD: dstore,\n\t\tC: *c,\n\t}, nil\n}\n\nfunc CreateOrLoadMockedRepo(dstore ipfs_ds.Batching) (ipfs_repo.Repo, error) {\n\tc, err := CreateBaseConfig()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &ipfs_repo.Mock{\n\t\tD: dstore,\n\t\tC: *c,\n\t}, nil\n}\n\nfunc LoadRepoFromPath(path string) (ipfs_repo.Repo, error) {\n\tdir, _ := filepath.Split(path)\n\tif _, err := LoadPlugins(dir); err != nil {\n\t\treturn nil, errors.Wrap(err, \"failed to load plugins\")\n\t}\n\n\tif !ipfs_fsrepo.IsInitialized(path) {\n\t\tcfg, err := CreateBaseConfig()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to create base config: %w\", err)\n\t\t}\n\n\t\tucfg, err := upgradeToPersistentConfig(cfg)\n\t\tif err != nil {\n\t\t\treturn nil, errors.Wrap(err, \"failed to upgrade repo\")\n\t\t}\n\n\t\tif err := ipfs_fsrepo.Init(path, ucfg); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to init ipfs repo: %w\", err)\n\t\t}\n\t}\n\n\treturn ipfs_fsrepo.Open(path)\n}\n\nvar DefaultSwarmListeners = []string{\n\t\"/ip4/0.0.0.0/udp/0/quic-v1\",\n\t\"/ip6/::/udp/0/quic-v1\",\n\t// \"/ip4/0.0.0.0/tcp/0\",\n\t// \"/ip6/::/tcp/0\",\n}\n\nfunc CreateBaseConfig() (*ipfs_cfg.Config, error) {\n\tc := ipfs_cfg.Config{}\n\n\t// set default bootstrap\n\tc.Bootstrap = ipfs_cfg.DefaultBootstrapAddresses\n\tc.Peering.Peers = []p2p_peer.AddrInfo{}\n\n\t// Identity\n\tif err := ResetRepoIdentity(&c); err != nil {\n\t\treturn nil, errcode.ErrCode_TODO.Wrap(err)\n\t}\n\n\t// Discovery\n\tc.Discovery.MDNS.Enabled = true\n\n\t// swarm listeners\n\tc.Addresses.Swarm = DefaultSwarmListeners\n\n\t// Swarm\n\tc.Swarm.RelayClient.Enabled = ipfs_cfg.True\n\tc.Swarm.ConnMgr = ipfs_cfg.ConnMgr{\n\t\tLowWater:    ipfs_cfg.NewOptionalInteger(defaultConnMgrLowWater),\n\t\tHighWater:   ipfs_cfg.NewOptionalInteger(defaultConnMgrHighWater),\n\t\tGracePeriod: ipfs_cfg.NewOptionalDuration(defaultConnMgrGracePeriod),\n\t\tType:        ipfs_cfg.NewOptionalString(\"basic\"),\n\t}\n\n\tc.Routing = ipfs_cfg.Routing{\n\t\tType: ipfs_cfg.NewOptionalString(\"dhtclient\"),\n\t}\n\n\treturn &c, nil\n}\n\nfunc ResetRepoIdentity(c *ipfs_cfg.Config) error {\n\tpriv, pub, err := p2p_ci.GenerateKeyPairWithReader(p2p_ci.Ed25519, 2048, crand.Reader) // nolint:staticcheck\n\tif err != nil {\n\t\treturn errcode.ErrCode_TODO.Wrap(err)\n\t}\n\n\tpid, err := p2p_peer.IDFromPublicKey(pub) // nolint:staticcheck\n\tif err != nil {\n\t\treturn errcode.ErrCode_TODO.Wrap(err)\n\t}\n\n\tprivkeyb, err := p2p_ci.MarshalPrivateKey(priv)\n\tif err != nil {\n\t\treturn errcode.ErrCode_TODO.Wrap(err)\n\t}\n\n\t// Identity\n\tc.Identity.PeerID = pid.String()\n\tc.Identity.PrivKey = base64.StdEncoding.EncodeToString(privkeyb)\n\n\treturn nil\n}\n\nfunc upgradeToPersistentConfig(cfg *ipfs_cfg.Config) (*ipfs_cfg.Config, error) {\n\tcfgCopy, err := cfg.Clone()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// setup the node mount points.\n\tcfgCopy.Mounts = ipfs_cfg.Mounts{\n\t\tIPFS: \"/ipfs\",\n\t\tIPNS: \"/ipns\",\n\t}\n\n\tcfgCopy.Ipns = ipfs_cfg.Ipns{\n\t\tResolveCacheSize: 128,\n\t}\n\n\tcfgCopy.Reprovider = ipfs_cfg.Reprovider{\n\t\tInterval: ipfs_cfg.NewOptionalDuration(time.Hour * 12),\n\t\tStrategy: ipfs_cfg.NewOptionalString(\"all\"),\n\t}\n\n\tcfgCopy.Datastore = ipfs_cfg.Datastore{\n\t\tStorageMax:         \"10GB\",\n\t\tStorageGCWatermark: 90, // 90%\n\t\tGCPeriod:           \"1h\",\n\t\tBloomFilterSize:    0,\n\t\tSpec: map[string]any{\n\t\t\t\"type\": \"mount\",\n\t\t\t\"mounts\": []any{\n\t\t\t\tmap[string]any{\n\t\t\t\t\t\"mountpoint\": \"/blocks\",\n\t\t\t\t\t\"type\":       \"measure\",\n\t\t\t\t\t\"prefix\":     \"flatfs.datastore\",\n\t\t\t\t\t\"child\": map[string]any{\n\t\t\t\t\t\t\"type\":      \"flatfs\",\n\t\t\t\t\t\t\"path\":      \"blocks\",\n\t\t\t\t\t\t\"sync\":      true,\n\t\t\t\t\t\t\"shardFunc\": \"/repo/flatfs/shard/v1/next-to-last/2\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tmap[string]any{\n\t\t\t\t\t\"mountpoint\": \"/\",\n\t\t\t\t\t\"type\":       \"measure\",\n\t\t\t\t\t\"prefix\":     \"leveldb.datastore\",\n\t\t\t\t\t\"child\": map[string]any{\n\t\t\t\t\t\t\"type\":        \"levelds\",\n\t\t\t\t\t\t\"path\":        \"datastore\",\n\t\t\t\t\t\t\"compression\": \"none\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\treturn cfgCopy, nil\n}\n\nfunc ResetExistingRepoIdentity(repo ipfs_repo.Repo) (ipfs_repo.Repo, error) {\n\tcfg, err := repo.Config()\n\tif err != nil {\n\t\t_ = repo.Close()\n\t\treturn nil, errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\n\tif err := ResetRepoIdentity(cfg); err != nil {\n\t\treturn nil, errcode.ErrCode_TODO.Wrap(err)\n\t}\n\n\tupdatedCfg, err := upgradeToPersistentConfig(cfg)\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"failed to upgrade repo\")\n\t}\n\n\terr = repo.SetConfig(updatedCfg)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\n\treturn repo, nil\n}\n\nfunc LoadPlugins(repoPath string) (*ipfs_loader.PluginLoader, error) { // nolint:unparam\n\tif plugins != nil {\n\t\treturn plugins, nil\n\t}\n\n\tpluginpath := filepath.Join(repoPath, \"plugins\")\n\n\tlp, err := ipfs_loader.NewPluginLoader(pluginpath)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err = lp.Initialize(); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err = lp.Inject(); err != nil {\n\t\treturn nil, err\n\t}\n\n\tplugins = lp\n\treturn lp, nil\n}\n"
  },
  {
    "path": "pkg/ipfsutil/testing.go",
    "content": "package ipfsutil\n\nimport (\n\t\"context\"\n\tcrand \"crypto/rand\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"net\"\n\t\"testing\"\n\n\trendezvous \"github.com/berty/go-libp2p-rendezvous\"\n\tp2p_rpdb \"github.com/berty/go-libp2p-rendezvous/db/sqlcipher\"\n\tds \"github.com/ipfs/go-datastore\"\n\tdsync \"github.com/ipfs/go-datastore/sync\"\n\tipfs_cfg \"github.com/ipfs/kubo/config\"\n\tipfs_core \"github.com/ipfs/kubo/core\"\n\tipfs_p2p \"github.com/ipfs/kubo/core/node/libp2p\"\n\tipfs_repo \"github.com/ipfs/kubo/repo\"\n\t\"github.com/libp2p/go-libp2p\"\n\tpubsub \"github.com/libp2p/go-libp2p-pubsub\"\n\tp2p_ci \"github.com/libp2p/go-libp2p/core/crypto\"\n\thost \"github.com/libp2p/go-libp2p/core/host\"\n\tp2pnetwork \"github.com/libp2p/go-libp2p/core/network\"\n\tp2p_peer \"github.com/libp2p/go-libp2p/core/peer\"\n\t\"github.com/libp2p/go-libp2p/core/peerstore\"\n\t\"github.com/libp2p/go-libp2p/core/protocol\"\n\tmocknet \"github.com/libp2p/go-libp2p/p2p/net/mock\"\n\tma \"github.com/multiformats/go-multiaddr\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/zap\"\n\n\tipfs_mobile \"berty.tech/weshnet/v2/pkg/ipfsutil/mobile\"\n\ttinder \"berty.tech/weshnet/v2/pkg/tinder\"\n)\n\n// CoreAPIMock implements ipfs.CoreAPI and adds some debugging helpers\ntype CoreAPIMock interface {\n\tAPI() ExtendedCoreAPI\n\n\tPubSub() *pubsub.PubSub\n\tTinder() *tinder.Service\n\tMockNetwork() mocknet.Mocknet\n\tMockNode() *ipfs_core.IpfsNode\n\tClose()\n}\n\nfunc getOrCreatePrivateKeyFromDatastore(t testing.TB, ctx context.Context, datastore ds.Datastore) p2p_ci.PrivKey {\n\tconst datastoreKeyForPrivateKey = \"p2p_private_key\"\n\n\tprivkeyb, err := datastore.Get(ctx, ds.NewKey(\"private_key\"))\n\tif err == ds.ErrNotFound {\n\t\tpriv, _, err := p2p_ci.GenerateKeyPairWithReader(p2p_ci.RSA, 2048, crand.Reader)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"failed to generate pair key: %v\", err)\n\t\t}\n\n\t\tprivkeyb, err := p2p_ci.MarshalPrivateKey(priv)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"failed to get raw priv key: %v\", err)\n\t\t}\n\n\t\tif err := datastore.Put(ctx, ds.NewKey(datastoreKeyForPrivateKey), privkeyb); err != nil {\n\t\t\tt.Fatalf(\"failed to save priv key: %v\", err)\n\t\t}\n\n\t\treturn priv\n\t} else if err != nil {\n\t\tt.Fatalf(\"failed to get value from datastore: %v\", err)\n\t}\n\n\tpriv, err := p2p_ci.UnmarshalPrivateKey(privkeyb)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to unmarshal priv key: %v\", err)\n\t}\n\n\treturn priv\n}\n\nfunc TestingRepo(t testing.TB, ctx context.Context, datastore ds.Datastore) ipfs_repo.Repo {\n\tt.Helper()\n\n\tc := ipfs_cfg.Config{}\n\tpriv := getOrCreatePrivateKeyFromDatastore(t, ctx, datastore)\n\n\tpid, err := p2p_peer.IDFromPublicKey(priv.GetPublic())\n\tif err != nil {\n\t\tt.Fatalf(\"failed to get pid from pub key: %v\", err)\n\t}\n\n\tprivkeyb, err := p2p_ci.MarshalPrivateKey(priv)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to get raw priv key: %v\", err)\n\t}\n\n\tc.Bootstrap = []string{}\n\tc.Addresses.Swarm = []string{\"/ip6/::/tcp/0\"}\n\tc.Identity.PeerID = pid.String()\n\tc.Identity.PrivKey = base64.StdEncoding.EncodeToString(privkeyb)\n\tc.Swarm.ResourceMgr.Enabled = ipfs_cfg.False\n\n\tif datastore == nil {\n\t\tdatastore = ds.NewMapDatastore()\n\t}\n\tdstore := dsync.MutexWrap(datastore)\n\n\treturn &ipfs_repo.Mock{\n\t\tD: dstore,\n\t\tC: c,\n\t}\n}\n\ntype TestingAPIOpts struct {\n\tLogger          *zap.Logger\n\tMocknet         mocknet.Mocknet\n\tDatastore       ds.Batching\n\tDiscoveryServer *tinder.MockDriverServer\n}\n\n// TestingCoreAPIUsingMockNet returns a fully initialized mocked Core API with the given mocknet\nfunc TestingCoreAPIUsingMockNet(ctx context.Context, t testing.TB, opts *TestingAPIOpts) CoreAPIMock {\n\tif opts.Logger == nil {\n\t\topts.Logger = zap.NewNop()\n\t}\n\n\tmsrv := opts.DiscoveryServer\n\tif msrv == nil {\n\t\tt.Log(\"warning: no discovery available\")\n\t\tmsrv = tinder.NewMockDriverServer()\n\t}\n\n\tdatastore := opts.Datastore\n\tif datastore == nil {\n\t\tdatastore = dsync.MutexWrap(ds.NewMapDatastore())\n\t}\n\n\trepo := TestingRepo(t, ctx, datastore)\n\n\tmrepo := ipfs_mobile.NewRepoMobile(\"\", repo)\n\tt.Cleanup(func() { mrepo.Close() })\n\n\tmnode, err := NewIPFSMobile(ctx, mrepo, &MobileOptions{\n\t\tHostOption:    MockHostOption(opts.Mocknet),\n\t\tRoutingOption: ipfs_p2p.NilRouterOption,\n\t\tExtraOpts: map[string]bool{\n\t\t\t\"pubsub\": false,\n\t\t},\n\t\tP2PStaticRelays: []string{},\n\t\tPeerStorePeers:  []string{},\n\t})\n\tt.Cleanup(func() { mnode.Close() })\n\th := mnode.PeerHost()\n\n\tmockDriver := msrv.Client(h)\n\n\t// enable discovery monitor\n\tstinder, err := tinder.NewService(h, opts.Logger, mockDriver)\n\trequire.NoError(t, err)\n\n\tdiscAdaptater := tinder.NewDiscoveryAdaptater(opts.Logger, stinder)\n\tt.Cleanup(func() { discAdaptater.Close() })\n\n\tps, err := pubsub.NewGossipSub(ctx, h, pubsub.WithDiscovery(discAdaptater))\n\trequire.NoError(t, err)\n\n\trequire.NoError(t, err, \"failed to initialize IPFS node mock\")\n\trequire.NotNil(t, ps, \"pubsub should not be nil\")\n\trequire.NotNil(t, stinder, \"discovery should not be nil\")\n\n\texapi, err := NewExtendedCoreAPIFromNode(mnode.IpfsNode)\n\trequire.NoError(t, err, \"unable to extend core api from node\")\n\n\tpsapi := NewPubSubAPI(ctx, opts.Logger, ps)\n\texapi = InjectPubSubCoreAPIExtendedAdapter(exapi, psapi)\n\tEnableConnLogger(ctx, opts.Logger, mnode.PeerHost())\n\n\tapi := &coreAPIMock{\n\t\tcoreapi: exapi,\n\t\tmocknet: opts.Mocknet,\n\t\tpubsub:  ps,\n\t\tnode:    mnode.IpfsNode,\n\t\ttinder:  stinder,\n\t}\n\n\treturn api\n}\n\n// TestingCoreAPI returns a fully initialized mocked Core API.\n// If you want to do some tests involving multiple peers you should use\n// `TestingCoreAPIUsingMockNet` with the same mocknet instead.\nfunc TestingCoreAPI(ctx context.Context, t testing.TB) CoreAPIMock {\n\tt.Helper()\n\n\tm := mocknet.New()\n\tt.Cleanup(func() { m.Close() })\n\n\tapi := TestingCoreAPIUsingMockNet(ctx, t, &TestingAPIOpts{\n\t\tMocknet:         m,\n\t\tDiscoveryServer: tinder.NewMockDriverServer(),\n\t})\n\n\treturn api\n}\n\nfunc TestingRDVP(ctx context.Context, t testing.TB, h host.Host) (*rendezvous.RendezvousService, func()) {\n\tdb, err := p2p_rpdb.OpenDB(ctx, \":memory:\")\n\trequire.NoError(t, err)\n\n\tprovider, err := rendezvous.NewSyncInMemProvider(h)\n\trequire.NoError(t, err)\n\n\tsvc := rendezvous.NewRendezvousService(h, db, provider)\n\tcleanup := func() {\n\t\t_ = db.Close() // dont use me for now as db is open in_memory\n\t}\n\treturn svc, cleanup\n}\n\ntype coreAPIMock struct {\n\tcoreapi ExtendedCoreAPI\n\n\tpubsub  *pubsub.PubSub\n\tmocknet mocknet.Mocknet\n\tnode    *ipfs_core.IpfsNode\n\ttinder  *tinder.Service\n}\n\nfunc (m *coreAPIMock) ConnMgr() ConnMgr {\n\treturn m.node.PeerHost.ConnManager()\n}\n\nfunc (m *coreAPIMock) NewStream(ctx context.Context, p p2p_peer.ID, pids ...protocol.ID) (p2pnetwork.Stream, error) {\n\treturn m.node.PeerHost.NewStream(ctx, p, pids...)\n}\n\nfunc (m *coreAPIMock) SetStreamHandler(pid protocol.ID, handler p2pnetwork.StreamHandler) {\n\tm.node.PeerHost.SetStreamHandler(pid, handler)\n}\n\nfunc (m *coreAPIMock) RemoveStreamHandler(pid protocol.ID) {\n\tm.node.PeerHost.RemoveStreamHandler(pid)\n}\n\nfunc (m *coreAPIMock) API() ExtendedCoreAPI {\n\treturn m.coreapi\n}\n\nfunc (m *coreAPIMock) MockNetwork() mocknet.Mocknet {\n\treturn m.mocknet\n}\n\nfunc (m *coreAPIMock) MockNode() *ipfs_core.IpfsNode {\n\treturn m.node\n}\n\nfunc (m *coreAPIMock) PubSub() *pubsub.PubSub {\n\treturn m.pubsub\n}\n\nfunc (m *coreAPIMock) Tinder() *tinder.Service {\n\treturn m.tinder\n}\n\nfunc (m *coreAPIMock) Close() {\n\tm.node.Close()\n}\n\nfunc MockHostOption(mn mocknet.Mocknet) ipfs_p2p.HostOption {\n\treturn func(id p2p_peer.ID, ps peerstore.Peerstore, _ ...libp2p.Option) (host.Host, error) {\n\t\tblackholeIP6 := net.ParseIP(\"100::\")\n\n\t\tpkey := ps.PrivKey(id)\n\t\tif pkey == nil {\n\t\t\treturn nil, fmt.Errorf(\"missing private key for node ID: %s\", id)\n\t\t}\n\n\t\tsuffix := id\n\t\tif len(id) > 8 {\n\t\t\tsuffix = id[len(id)-8:]\n\t\t}\n\t\tip := append(net.IP{}, blackholeIP6...)\n\t\tcopy(ip[net.IPv6len-len(suffix):], suffix)\n\t\ta, err := ma.NewMultiaddr(fmt.Sprintf(\"/ip6/%s/tcp/4242\", ip))\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to create test multiaddr: %s\", err)\n\t\t}\n\n\t\tps.AddAddr(id, a, peerstore.PermanentAddrTTL)\n\t\terr = ps.AddPrivKey(id, pkey)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\terr = ps.AddPubKey(id, pkey.GetPublic())\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\treturn mn.AddPeerWithPeerstore(id, ps)\n\t}\n}\n"
  },
  {
    "path": "pkg/ipfsutil/util.go",
    "content": "package ipfsutil\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"net\"\n\t\"sort\"\n\n\tma \"github.com/multiformats/go-multiaddr\"\n\t\"go.uber.org/multierr\"\n)\n\ntype Multiaddrs []ma.Multiaddr\n\nfunc NewMultiaddrs(m []ma.Multiaddr) Multiaddrs {\n\tms := Multiaddrs(m)\n\tsort.Sort(ms)\n\treturn ms\n}\n\n// Len is the number of elements in the collection.\nfunc (ms Multiaddrs) Len() int { return len(ms) }\n\n// Less reports whether the element with\n// index i should sort before the element with index j.\nfunc (ms Multiaddrs) Less(i, j int) bool { return bytes.Compare(ms[i].Bytes(), ms[j].Bytes()) < 0 }\n\n// Swap swaps the elements with indexes i and j.\nfunc (ms Multiaddrs) Swap(i, j int) { ms[i], ms[j] = ms[j], ms[i] }\n\n// MultiaddrIsEqual return true if both slice are equal\nfunc MultiaddrIsEqual(a Multiaddrs, b Multiaddrs) bool {\n\tif len(a) != len(b) {\n\t\treturn false\n\t}\n\n\tfor i := range a {\n\t\tif !a[i].Equal(b[i]) {\n\t\t\treturn false\n\t\t}\n\t}\n\n\treturn true\n}\n\nfunc ParseAddr(addr string) (ma.Multiaddr, error) {\n\tmaddr, err := ma.NewMultiaddr(addr)\n\tif err == nil {\n\t\treturn maddr, nil\n\t}\n\n\t// try to get a tcp multiaddr from host:port\n\thost, port, err := net.SplitHostPort(addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif host == \"\" {\n\t\thost = \"127.0.0.1\"\n\t}\n\n\taddr = fmt.Sprintf(\"/ip4/%s/tcp/%s/\", host, port)\n\treturn ma.NewMultiaddr(addr)\n}\n\nfunc ParseAddrs(addrs ...string) ([]ma.Multiaddr, error) {\n\tmaddrs := make([]ma.Multiaddr, len(addrs))\n\n\tvar err error\n\tfor i, addr := range addrs {\n\t\tvar thisErr error\n\t\tmaddrs[i], thisErr = ParseAddr(addr)\n\t\terr = multierr.Append(err, thisErr)\n\t}\n\n\treturn maddrs, err\n}\n"
  },
  {
    "path": "pkg/lifecycle/example_test.go",
    "content": "package lifecycle_test\n"
  },
  {
    "path": "pkg/lifecycle/manager.go",
    "content": "package lifecycle\n\nimport (\n\t\"context\"\n\t\"sync\"\n\n\t\"berty.tech/weshnet/v2/internal/notify\"\n)\n\ntype State int\n\nconst (\n\tStateActive State = iota\n\tStateInactive\n)\n\ntype Manager struct {\n\tcurrentState State\n\n\tlocker *sync.RWMutex\n\tnotify *notify.Notify\n\n\tgroupWaiter sync.WaitGroup\n}\n\nfunc NewManager(initialState State) *Manager {\n\tvar locker sync.RWMutex\n\treturn &Manager{\n\t\tcurrentState: initialState,\n\t\tlocker:       &locker,\n\t\tnotify:       notify.New(&locker),\n\t}\n}\n\n// UpdateState update the current state of the Manager\nfunc (m *Manager) UpdateState(state State) {\n\tm.locker.Lock()\n\tif m.currentState != state {\n\t\tm.currentState = state\n\t\tm.notify.Broadcast()\n\t}\n\tm.locker.Unlock()\n}\n\n// WaitForStateChange waits until the currentState changes from sourceState or ctx expires. A true value is returned in former case and false in latter.\nfunc (m *Manager) WaitForStateChange(ctx context.Context, sourceState State) bool {\n\tm.locker.Lock()\n\n\tok := true\n\tfor sourceState == m.currentState && ok {\n\t\t// wait until state has been changed or context has been cancel\n\t\tok = m.notify.Wait(ctx)\n\t}\n\n\tm.locker.Unlock()\n\treturn ok\n}\n\n// GetCurrentState return the current state of the Manager\nfunc (m *Manager) GetCurrentState() (state State) {\n\tm.locker.RLock()\n\tstate = m.currentState\n\tm.locker.RUnlock()\n\treturn\n}\n\n// TaskWaitForStateChange is the same as `WaitForStateChange` but also return a\n// task that can be mark as done by the upper caller to notify he is done\n// handling the new state, done should always be called to avoid deadlock.\n// use `WaitForTasks` to wait for task to complete\nfunc (m *Manager) TaskWaitForStateChange(ctx context.Context, sourceState State) (Task, bool) {\n\tm.locker.Lock()\n\tdefer m.locker.Unlock()\n\n\tfor sourceState == m.currentState {\n\t\t// wait until state has been changed or context has been cancel\n\t\tif ok := m.notify.Wait(ctx); !ok {\n\t\t\treturn nil, false\n\t\t}\n\t}\n\n\tm.groupWaiter.Add(1)\n\treturn &task{t: &m.groupWaiter}, true\n}\n\n// WaitForTasks wait until all tasks returned by `TaskWaitForStateChange` are done\nfunc (m *Manager) WaitForTasks() {\n\tm.locker.RLock()\n\tm.groupWaiter.Wait() // wait for all tasks to be done\n\tm.locker.RUnlock()\n}\n"
  },
  {
    "path": "pkg/lifecycle/task.go",
    "content": "package lifecycle\n\nimport \"sync\"\n\ntype Task interface {\n\tDone()\n}\n\ntype task struct {\n\tt Task\n\to sync.Once\n}\n\nfunc (t *task) Done() {\n\tt.o.Do(t.t.Done)\n}\n"
  },
  {
    "path": "pkg/logutil/crypto_utils.go",
    "content": "package logutil\n\nimport (\n\t\"encoding/base64\"\n\n\t\"github.com/libp2p/go-libp2p/core/crypto\"\n)\n\nfunc CryptoKeyToBytes(key crypto.Key) []byte {\n\tkeyBytes, err := key.Raw()\n\tif err != nil {\n\t\treturn []byte{0}\n\t}\n\n\treturn keyBytes\n}\n\nfunc CryptoKeyToBase64(key crypto.Key) string {\n\tbytes := CryptoKeyToBytes(key)\n\treturn base64.StdEncoding.EncodeToString(bytes)\n}\n"
  },
  {
    "path": "pkg/logutil/encoders.go",
    "content": "package logutil\n\nimport (\n\t\"fmt\"\n\n\t\"go.uber.org/zap/zapcore\"\n)\n\nconst (\n\tBlack uint8 = iota + 30\n\tRed\n\tGreen\n\tYellow\n\tBlue\n\tMagenta\n\tCyan\n\tWhite\n)\n\nfunc stableWidthNameEncoder(loggerName string, enc zapcore.PrimitiveArrayEncoder) {\n\tenc.AppendString(fmt.Sprintf(\"%-18s\", loggerName))\n}\n\nfunc stableWidthCapitalLevelEncoder(l zapcore.Level, enc zapcore.PrimitiveArrayEncoder) {\n\tenc.AppendString(fmt.Sprintf(\"%-5s\", l.CapitalString()))\n}\n\nfunc stableWidthCapitalColorLevelEncoder(l zapcore.Level, enc zapcore.PrimitiveArrayEncoder) {\n\tswitch l {\n\tcase zapcore.DebugLevel:\n\t\tenc.AppendString(fmt.Sprintf(\"\\x1b[%dm%s\\x1b[0m\", Magenta, \"DEBUG\"))\n\tcase zapcore.InfoLevel:\n\t\tenc.AppendString(fmt.Sprintf(\"\\x1b[%dm%s\\x1b[0m\", Blue, \"INFO\"))\n\tcase zapcore.WarnLevel:\n\t\tenc.AppendString(fmt.Sprintf(\"\\x1b[%dm%s\\x1b[0m\", Yellow, \"WARN\"))\n\tcase zapcore.ErrorLevel:\n\t\tenc.AppendString(fmt.Sprintf(\"\\x1b[%dm%s\\x1b[0m\", Red, \"ERROR\"))\n\tcase zapcore.DPanicLevel:\n\t\tenc.AppendString(fmt.Sprintf(\"\\x1b[%dm%s\\x1b[0m\", Red, \"DPANIC\"))\n\tcase zapcore.PanicLevel:\n\t\tenc.AppendString(fmt.Sprintf(\"\\x1b[%dm%s\\x1b[0m\", Red, \"PANIC\"))\n\tcase zapcore.FatalLevel:\n\t\tenc.AppendString(fmt.Sprintf(\"\\x1b[%dm%s\\x1b[0m\", Red, \"FATAL\"))\n\tdefault:\n\t\tenc.AppendString(fmt.Sprintf(\"\\x1b[%dm%s\\x1b[0m\", Red, l.CapitalString()))\n\t}\n}\n"
  },
  {
    "path": "pkg/logutil/example_test.go",
    "content": "package logutil_test\n\nimport (\n\t\"berty.tech/weshnet/v2/pkg/logutil\"\n)\n\nfunc Example_logall() {\n\tlogger, cleanup, err := logutil.NewLogger(logutil.NewStdStream(\"*\", \"light-console\", \"stdout\"))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer cleanup()\n\n\tlogger.Debug(\"top debug\")\n\tlogger.Info(\"top info\")\n\tlogger.Warn(\"top warn\")\n\tlogger.Error(\"top error\")\n\n\tlogger.Named(\"foo\").Debug(\"foo debug\")\n\tlogger.Named(\"foo\").Info(\"foo info\")\n\tlogger.Named(\"foo\").Warn(\"foo warn\")\n\tlogger.Named(\"foo\").Error(\"foo error\")\n\n\t// Output:\n\t// DEBUG\tbty               \tlogutil/example_test.go:14\ttop debug\n\t// INFO \tbty               \tlogutil/example_test.go:15\ttop info\n\t// WARN \tbty               \tlogutil/example_test.go:16\ttop warn\n\t// ERROR\tbty               \tlogutil/example_test.go:17\ttop error\n\t// DEBUG\tbty.foo           \tlogutil/example_test.go:19\tfoo debug\n\t// INFO \tbty.foo           \tlogutil/example_test.go:20\tfoo info\n\t// WARN \tbty.foo           \tlogutil/example_test.go:21\tfoo warn\n\t// ERROR\tbty.foo           \tlogutil/example_test.go:22\tfoo error\n}\n\nfunc Example_logerrors() {\n\tlogger, cleanup, err := logutil.NewLogger(logutil.NewStdStream(\"error:*,-*.bar warn:*.bar\", \"light-console\", \"stdout\"))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer cleanup()\n\n\tlogger.Debug(\"top debug\")\n\tlogger.Info(\"top info\")\n\tlogger.Warn(\"top warn\")\n\tlogger.Error(\"top error\")\n\n\tlogger.Named(\"foo\").Debug(\"foo debug\")\n\tlogger.Named(\"foo\").Info(\"foo info\")\n\tlogger.Named(\"foo\").Warn(\"foo warn\")\n\tlogger.Named(\"foo\").Error(\"foo error\")\n\n\tlogger.Named(\"foo\").Named(\"bar\").Debug(\"foo.bar debug\")\n\tlogger.Named(\"foo\").Named(\"bar\").Info(\"foo.bar info\")\n\tlogger.Named(\"foo\").Named(\"bar\").Warn(\"foo.bar warn\")\n\tlogger.Named(\"foo\").Named(\"bar\").Error(\"foo.bar error\")\n\n\t// Output:\n\t// ERROR\tbty               \tlogutil/example_test.go:45\ttop error\n\t// ERROR\tbty.foo           \tlogutil/example_test.go:50\tfoo error\n\t// WARN \tbty.foo.bar       \tlogutil/example_test.go:54\tfoo.bar warn\n}\n"
  },
  {
    "path": "pkg/logutil/file.go",
    "content": "package logutil\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"sort\"\n\t\"strings\"\n\t\"time\"\n\n\t\"go.uber.org/multierr\"\n\t\"moul.io/u\"\n\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n)\n\nfunc newFileWriteCloser(target, kind string) (io.WriteCloser, error) {\n\tvar filename string\n\tswitch {\n\tcase strings.HasSuffix(target, \".log\"): // use the indicated 'path' as filename\n\t\tfilename = target\n\tdefault: // automatically create a new file in the 'path' directory following a pattern\n\t\tstartTime := time.Now().Format(filePatternDateLayout)\n\t\tfilename = filepath.Join(\n\t\t\ttarget,\n\t\t\tfmt.Sprintf(\"%s-%s.log\", kind, startTime),\n\t\t)\n\t\t// run gc\n\t\t{\n\t\t\terr := LogfileGC(target, 20)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t}\n\n\tif dir := filepath.Dir(filename); !u.DirExists(dir) {\n\t\terr := os.MkdirAll(dir, 0o711)\n\t\tif err != nil {\n\t\t\treturn nil, errcode.ErrCode_TODO.Wrap(err)\n\t\t}\n\t}\n\n\tvar writer io.WriteCloser\n\tif u.FileExists(filename) {\n\t\tvar err error\n\t\twriter, err = os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, os.ModeAppend)\n\t\tif err != nil {\n\t\t\treturn nil, errcode.ErrCode_TODO.Wrap(err)\n\t\t}\n\t} else {\n\t\tvar err error\n\t\twriter, err = os.Create(filename)\n\t\tif err != nil {\n\t\t\treturn nil, errcode.ErrCode_TODO.Wrap(err)\n\t\t}\n\t}\n\n\treturn writer, nil\n}\n\ntype Logfile struct {\n\tDir    string\n\tName   string\n\tSize   int64\n\tKind   string\n\tTime   time.Time\n\tLatest bool\n\tErrs   error `json:\"Errs,omitempty\"`\n}\n\nfunc (l Logfile) Path() string {\n\treturn filepath.Join(l.Dir, l.Name)\n}\n\nconst filePatternDateLayout = \"2006-01-02T15-04-05.000\"\n\nvar filePatternRegex = regexp.MustCompile(`(?m)^(.*)-(\\d{4}-\\d{2}-\\d{2}T\\d{2}-\\d{2}-\\d{2}.\\d{3}).log$`)\n\nfunc LogfileList(logDir string) ([]*Logfile, error) {\n\tfiles, err := os.ReadDir(logDir)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_TODO.Wrap(err)\n\t}\n\n\tinfos := make([]fs.FileInfo, 0, len(files))\n\tfor _, entry := range files {\n\t\tinfo, err := entry.Info()\n\t\tif err != nil {\n\t\t\treturn nil, errcode.ErrCode_TODO.Wrap(err)\n\t\t}\n\t\tinfos = append(infos, info)\n\t}\n\n\tlogfiles := []*Logfile{}\n\tfor _, info := range infos {\n\t\tsub := filePatternRegex.FindStringSubmatch(info.Name())\n\t\tif sub == nil {\n\t\t\tcontinue\n\t\t}\n\t\tt, err := time.Parse(filePatternDateLayout, sub[2])\n\t\tvar errs error\n\t\tif err != nil {\n\t\t\terrs = multierr.Append(errs, err)\n\t\t}\n\n\t\t// use os.Stat to get the file size (updated than fs.FileInfo.Size()\n\t\tfilepath := filepath.Join(logDir, info.Name())\n\t\tfi, err := os.Stat(filepath)\n\t\tif err != nil {\n\t\t\terrs = multierr.Append(errs, err)\n\t\t}\n\n\t\tlogfiles = append(logfiles, &Logfile{\n\t\t\tDir:  logDir,\n\t\t\tName: info.Name(),\n\t\t\tSize: fi.Size(),\n\t\t\tKind: sub[1],\n\t\t\tTime: t,\n\t\t\tErrs: errs,\n\t\t})\n\t}\n\n\t// compute latest\n\tif len(logfiles) > 0 {\n\t\tvar maxTime time.Time\n\t\tfor _, file := range logfiles {\n\t\t\tif file.Time.After(maxTime) {\n\t\t\t\tmaxTime = file.Time\n\t\t\t}\n\t\t}\n\t\tfor _, file := range logfiles {\n\t\t\tif file.Time == maxTime {\n\t\t\t\tfile.Latest = true\n\t\t\t}\n\t\t}\n\t}\n\n\treturn logfiles, nil\n}\n\nfunc CurrentLogfilePath(target string) (string, error) {\n\tfilename := \"\"\n\n\tswitch {\n\tcase strings.HasSuffix(target, \".log\"): // use the indicated 'path' as filename\n\t\tfilename = target\n\tdefault: // find the latest log file in the 'path' directory following a pattern\n\t\tlogfileList, err := LogfileList(target)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\n\t\tfor _, logfile := range logfileList {\n\t\t\tif logfile.Latest {\n\t\t\t\tfilename = logfile.Path()\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\n\treturn filename, nil\n}\n\nfunc LogfileGC(logDir string, max int) error {\n\tif !u.DirExists(logDir) {\n\t\treturn nil\n\t}\n\tfiles, err := LogfileList(logDir)\n\tif err != nil {\n\t\treturn errcode.ErrCode_TODO.Wrap(err)\n\t}\n\tif len(files) < max {\n\t\treturn nil\n\t}\n\n\tsort.Slice(files, func(i, j int) bool {\n\t\treturn files[i].Time.Before(files[j].Time)\n\t})\n\n\tvar errs error\n\tfor i := 0; i < len(files)-max; i++ {\n\t\terr := os.Remove(files[i].Path())\n\t\tif err != nil {\n\t\t\terrs = multierr.Append(errs, err)\n\t\t}\n\t}\n\treturn errs\n}\n"
  },
  {
    "path": "pkg/logutil/file_test.go",
    "content": "package logutil\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/require\"\n\t\"moul.io/u\"\n)\n\nfunc TestLogfile(t *testing.T) {\n\t// setup volatile directory for the test\n\ttempdir, err := os.MkdirTemp(\"\", \"logutil-file\")\n\trequire.NoError(t, err)\n\tdefer os.RemoveAll(tempdir)\n\n\t// check loading log files from an invalid directory\n\t{\n\t\tfiles, err := LogfileList(filepath.Join(tempdir, \"doesnotexist\"))\n\t\trequire.Error(t, err)\n\t\trequire.Nil(t, files)\n\t}\n\n\t// check loading files from empty valid directory\n\t{\n\t\tfiles, err := LogfileList(tempdir)\n\t\trequire.NoError(t, err)\n\t\trequire.Empty(t, files)\n\t}\n\n\t// create dummy files\n\t{\n\t\tdummyNames := []string{\n\t\t\t\"2021-05-25T21-12-02.650.log\",\n\t\t\t\"cli.info-2021-05-25T21-12-02.aaa.log\",\n\t\t\t\"blah.log\",\n\t\t}\n\t\tfor _, name := range dummyNames {\n\t\t\tf, err := os.Create(filepath.Join(tempdir, name))\n\t\t\trequire.NoError(t, err)\n\t\t\terr = f.Close()\n\t\t\trequire.NoError(t, err)\n\t\t}\n\t}\n\n\t// check loading files from valid directory with only dummy files\n\t{\n\t\tfiles, err := LogfileList(tempdir)\n\t\trequire.NoError(t, err)\n\t\trequire.Empty(t, files)\n\t}\n\n\t// create a first logger of kind-1\n\t{\n\t\twriter, err := newFileWriteCloser(tempdir, \"kind-1\")\n\t\trequire.NoError(t, err)\n\t\trequire.NotNil(t, writer)\n\t\t_, err = writer.Write([]byte(\"blah\\n\"))\n\t\trequire.NoError(t, err)\n\t\terr = writer.Close()\n\t\trequire.NoError(t, err)\n\t}\n\n\t// check loading files from the directory, should have one now\n\t{\n\t\tfiles, err := LogfileList(tempdir)\n\t\trequire.NoError(t, err)\n\t\trequire.Len(t, files, 1)\n\t\trequire.Equal(t, files[0].Dir, tempdir)\n\t\trequire.NotEmpty(t, files[0].Name)\n\t\trequire.Equal(t, files[0].Path(), filepath.Join(tempdir, files[0].Name))\n\t\trequire.True(t, u.FileExists(files[0].Path()))\n\t\trequire.True(t, files[0].Latest)\n\t\trequire.Equal(t, files[0].Kind, \"kind-1\")\n\t}\n\n\t// create a second logger of kind-1\n\t{\n\t\ttime.Sleep(time.Second)\n\t\twriter, err := newFileWriteCloser(tempdir, \"kind-1\")\n\t\trequire.NoError(t, err)\n\t\trequire.NotNil(t, writer)\n\t\t_, err = writer.Write([]byte(\"blah blah\\n\"))\n\t\trequire.NoError(t, err)\n\t\terr = writer.Close()\n\t\trequire.NoError(t, err)\n\t}\n\n\t// check loading files from the directory, should have two now\n\t{\n\t\tfiles, err := LogfileList(tempdir)\n\t\trequire.NoError(t, err)\n\t\trequire.Len(t, files, 2)\n\t\tfor _, file := range files {\n\t\t\trequire.Equal(t, file.Dir, tempdir)\n\t\t\trequire.NotEmpty(t, file.Name)\n\t\t\trequire.Equal(t, file.Path(), filepath.Join(tempdir, file.Name))\n\t\t\trequire.True(t, u.FileExists(file.Path()))\n\t\t}\n\t}\n\n\t// try to gc with fewer files than the limit\n\t{\n\t\terr := LogfileGC(tempdir, 10)\n\t\trequire.NoError(t, err)\n\t}\n\n\t// create 10 new files\n\t{\n\t\tfor i := 0; i < 10; i++ {\n\t\t\twriter, err := newFileWriteCloser(tempdir, fmt.Sprintf(\"hello-%d\", i))\n\t\t\trequire.NoError(t, err)\n\t\t\terr = writer.Close()\n\t\t\trequire.NoError(t, err)\n\t\t}\n\t}\n\n\t// check loading files from the directory, should have twelve now\n\t{\n\t\tfiles, err := LogfileList(tempdir)\n\t\trequire.NoError(t, err)\n\t\trequire.Len(t, files, 12)\n\t\tfor _, file := range files {\n\t\t\trequire.Equal(t, file.Dir, tempdir)\n\t\t\trequire.NotEmpty(t, file.Name)\n\t\t\trequire.Equal(t, file.Path(), filepath.Join(tempdir, file.Name))\n\t\t\trequire.True(t, u.FileExists(file.Path()))\n\t\t}\n\t}\n\n\t// try to gc with fewer files than the limit\n\t{\n\t\terr := LogfileGC(tempdir, 10)\n\t\trequire.NoError(t, err)\n\t}\n\n\t// check loading files from the directory, should have ten now\n\t{\n\t\tfiles, err := LogfileList(tempdir)\n\t\trequire.NoError(t, err)\n\t\trequire.Len(t, files, 10)\n\t}\n\n\t// try to gc with the current amount of files\n\t{\n\t\terr := LogfileGC(tempdir, 10)\n\t\trequire.NoError(t, err)\n\t}\n\n\t// check loading files from the directory, should still have ten\n\t{\n\t\tfiles, err := LogfileList(tempdir)\n\t\trequire.NoError(t, err)\n\t\trequire.Len(t, files, 10)\n\t}\n\n\t// try to gc with only one\n\t{\n\t\terr := LogfileGC(tempdir, 1)\n\t\trequire.NoError(t, err)\n\t}\n\n\t// check loading files from the directory, should now have only one\n\t{\n\t\tfiles, err := LogfileList(tempdir)\n\t\trequire.NoError(t, err)\n\t\trequire.Len(t, files, 1)\n\t}\n}\n"
  },
  {
    "path": "pkg/logutil/grpc_logger.go",
    "content": "package logutil\n\nimport (\n\t\"sync\"\n\n\tgrpc_zap \"github.com/grpc-ecosystem/go-grpc-middleware/logging/zap\"\n\t\"go.uber.org/zap\"\n)\n\nvar (\n\t// grpc logger should be set only once.\n\t// without this singleton, we can raise race conditions in unit tests => https://github.com/grpc/grpc-go/issues/1084\n\tgrpcLoggerConfigured   bool\n\tmuGRPCLoggerConfigured sync.Mutex\n)\n\nfunc ReplaceGRPCLogger(l *zap.Logger) {\n\tmuGRPCLoggerConfigured.Lock()\n\n\tif !grpcLoggerConfigured {\n\t\tgrpc_zap.ReplaceGrpcLoggerV2(l)\n\t\tgrpcLoggerConfigured = true\n\t}\n\n\tmuGRPCLoggerConfigured.Unlock()\n}\n"
  },
  {
    "path": "pkg/logutil/logger_native.go",
    "content": "package logutil\n\nimport (\n\t\"fmt\"\n\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\ntype nativeCore struct {\n\tsubsystem string\n\tenc       zapcore.Encoder\n}\n\nfunc NewNativeDriverCore(subsystem string, enc zapcore.Encoder) zapcore.Core {\n\treturn &nativeCore{subsystem: subsystem, enc: enc}\n}\n\nfunc (nc *nativeCore) Enabled(zapcore.Level) bool {\n\treturn true\n}\n\nfunc (nc *nativeCore) With([]zapcore.Field) zapcore.Core {\n\treturn &nativeCore{enc: nc.enc}\n}\n\nfunc (nc *nativeCore) Check(entry zapcore.Entry, checked *zapcore.CheckedEntry) *zapcore.CheckedEntry {\n\treturn checked.AddCore(entry, nc)\n}\n\nfunc (nc *nativeCore) Write(entry zapcore.Entry, fields []zapcore.Field) error {\n\tbuff, err := nc.enc.EncodeEntry(entry, fields)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tNativeLog(\n\t\tentry.Level,\n\t\tfmt.Sprintf(\"%s.%s\", nc.subsystem, entry.LoggerName),\n\t\tbuff.String(),\n\t)\n\treturn nil\n}\n\nfunc (nc *nativeCore) Sync() error {\n\treturn nil\n}\n\nfunc NewNativeLogger(subsystem string) *zap.Logger {\n\t// native logger\n\tnativeEncoderConfig := zap.NewDevelopmentEncoderConfig()\n\tnativeEncoderConfig.LevelKey = \"\"\n\tnativeEncoderConfig.TimeKey = \"\"\n\tnativeEncoderConfig.NameKey = \"\"\n\tnativeEncoderConfig.CallerKey = \"\"\n\n\tnativeEncoder := zapcore.NewConsoleEncoder(nativeEncoderConfig)\n\n\tcore := NewNativeDriverCore(subsystem, nativeEncoder)\n\n\t// create logger\n\tlogger := zap.New(core)\n\n\treturn logger\n}\n"
  },
  {
    "path": "pkg/logutil/logger_native_android.go",
    "content": "//go:build android\n\npackage logutil\n\n/*\n#cgo LDFLAGS: -llog\n#include <android/log.h>\n*/\nimport \"C\"\n\nimport (\n\t\"fmt\"\n\n\t\"go.uber.org/zap/zapcore\"\n)\n\nfunc NativeLog(logLevel zapcore.Level, namespace string, message string) {\n\tvar level C.int = C.ANDROID_LOG_INFO\n\n\tswitch logLevel {\n\tcase zapcore.DebugLevel:\n\t\tlevel = C.ANDROID_LOG_DEBUG\n\tcase zapcore.InfoLevel:\n\t\tlevel = C.ANDROID_LOG_INFO\n\tcase zapcore.WarnLevel:\n\t\tlevel = C.ANDROID_LOG_WARN\n\tcase zapcore.ErrorLevel:\n\t\tlevel = C.ANDROID_LOG_ERROR\n\t}\n\n\tC.__android_log_write(level, C.CString(namespace), C.CString(fmt.Sprintf(\"[%s] %s\", logLevel.CapitalString(), message)))\n}\n"
  },
  {
    "path": "pkg/logutil/logger_native_darwin.go",
    "content": "//go:build darwin\n// +build darwin\n\npackage logutil\n\n/*\n#import <os/log.h>\n\nconst int DEBUG = 0;\nconst int INFO = 1;\nconst int WARN = 2;\nconst int ERROR = 3;\n\nvoid os_log_wrapper(int level, os_log_t log, char *s) {\n\tswitch (level) {\n\t\tcase DEBUG:\n\t\t\tos_log_debug(log, \"%{public}s\", s);\n\t\t\tbreak ;\n\t\tcase WARN:\n\t\t\tos_log_error(log, \"%{public}s\", s);\n\t\t\tbreak ;\n\t\tcase ERROR:\n\t\t\tos_log_fault(log, \"%{public}s\", s);\n\t\t\tbreak ;\n\t\tdefault:\n\t\t\tos_log_info(log, \"%{public}s\", s);\n\t}\n}\n*/\nimport \"C\"\n\nimport (\n\t\"fmt\"\n\n\t\"go.uber.org/zap/zapcore\"\n)\n\nfunc NativeLog(logLevel zapcore.Level, namespace string, message string) {\n\tvar level C.int = C.INFO\n\n\tswitch logLevel {\n\tcase zapcore.DebugLevel:\n\t\tlevel = C.DEBUG\n\tcase zapcore.WarnLevel:\n\t\tlevel = C.WARN\n\tcase zapcore.ErrorLevel:\n\t\tlevel = C.ERROR\n\tdefault:\n\t\tlevel = C.INFO\n\t}\n\n\tlog := C.os_log_create(C.CString(namespace), C.CString(namespace))\n\n\tC.os_log_wrapper(level, log, C.CString(fmt.Sprintf(\"[%s] %s\", logLevel.CapitalString(), message)))\n}\n"
  },
  {
    "path": "pkg/logutil/logger_native_other.go",
    "content": "//go:build !darwin && !android\n\npackage logutil\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\t\"go.uber.org/zap/zapcore\"\n)\n\nfunc NativeLog(logLevel zapcore.Level, namespace string, message string) {\n\tfmt.Fprintf(os.Stderr, \"[%s] [%s] %s\\n\", logLevel.CapitalString(), namespace, message)\n}\n"
  },
  {
    "path": "pkg/logutil/logutil.go",
    "content": "package logutil\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\n\tipfs_log \"github.com/ipfs/go-log/v2\"\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n\t\"moul.io/u\"\n\t\"moul.io/zapfilter\"\n\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n)\n\nconst (\n\tconsoleEncoding = \"console\"\n\tjsonEncoding    = \"json\"\n)\n\nfunc NewLogger(streams ...Stream) (*zap.Logger, func(), error) {\n\tcores := []zapcore.Core{}\n\tcleanup := func() {}\n\twithIPFS := false\n\twithIPFSDebug := false\n\n\tfor _, opts := range streams {\n\t\tif opts.filters == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tvar core zapcore.Core\n\n\t\t// configure zap\n\t\tvar config zap.Config\n\t\tswitch strings.ToLower(opts.format) {\n\t\tcase \"\":\n\t\t\tconfig = zap.NewProductionConfig()\n\t\tcase \"json\":\n\t\t\tconfig = zap.NewProductionConfig()\n\t\t\tconfig.Development = true\n\t\t\tconfig.Encoding = jsonEncoding\n\t\tcase \"light-json\":\n\t\t\tconfig = zap.NewProductionConfig()\n\t\t\tconfig.Encoding = jsonEncoding\n\t\t\tconfig.EncoderConfig.TimeKey = \"\"\n\t\t\tconfig.EncoderConfig.EncodeLevel = stableWidthCapitalLevelEncoder\n\t\t\tconfig.Development = false\n\t\t\tconfig.DisableStacktrace = true\n\t\t\tconfig.Sampling = &zap.SamplingConfig{Initial: 100, Thereafter: 100}\n\t\tcase \"light-console\":\n\t\t\tconfig = zap.NewDevelopmentConfig()\n\t\t\tconfig.Encoding = consoleEncoding\n\t\t\tconfig.EncoderConfig.TimeKey = \"\"\n\t\t\tconfig.EncoderConfig.EncodeLevel = stableWidthCapitalLevelEncoder\n\t\t\tconfig.DisableStacktrace = true\n\t\t\tconfig.EncoderConfig.EncodeName = stableWidthNameEncoder\n\t\t\tconfig.Development = false\n\t\t\tconfig.Sampling = &zap.SamplingConfig{Initial: 100, Thereafter: 100}\n\t\tcase \"light-color\":\n\t\t\tconfig = zap.NewDevelopmentConfig()\n\t\t\tconfig.Encoding = consoleEncoding\n\t\t\tconfig.EncoderConfig.TimeKey = \"\"\n\t\t\tconfig.EncoderConfig.EncodeLevel = stableWidthCapitalColorLevelEncoder\n\t\t\tconfig.DisableStacktrace = true\n\t\t\tconfig.EncoderConfig.EncodeName = stableWidthNameEncoder\n\t\t\tconfig.Development = false\n\t\t\tconfig.Sampling = &zap.SamplingConfig{Initial: 100, Thereafter: 100}\n\t\tcase \"console\":\n\t\t\tconfig = zap.NewDevelopmentConfig()\n\t\t\tconfig.Encoding = consoleEncoding\n\t\t\tconfig.EncoderConfig.EncodeTime = zapcore.RFC3339TimeEncoder\n\t\t\tconfig.EncoderConfig.EncodeLevel = stableWidthCapitalLevelEncoder\n\t\t\tconfig.DisableStacktrace = true\n\t\t\tconfig.EncoderConfig.EncodeName = stableWidthNameEncoder\n\t\t\tconfig.Development = true\n\t\tcase \"color\":\n\t\t\tconfig = zap.NewDevelopmentConfig()\n\t\t\tconfig.Encoding = consoleEncoding\n\t\t\tconfig.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder\n\t\t\tconfig.EncoderConfig.EncodeDuration = zapcore.StringDurationEncoder\n\t\t\tconfig.EncoderConfig.EncodeLevel = stableWidthCapitalColorLevelEncoder\n\t\t\tconfig.DisableStacktrace = true\n\t\t\tconfig.EncoderConfig.EncodeName = stableWidthNameEncoder\n\t\t\tconfig.Development = true\n\t\tdefault:\n\t\t\treturn nil, nil, fmt.Errorf(\"unknown log format: %q\", opts.format)\n\t\t}\n\t\tconfig.Level = zap.NewAtomicLevelAt(zap.DebugLevel)\n\n\t\tvar enc zapcore.Encoder\n\t\tswitch config.Encoding {\n\t\tcase consoleEncoding:\n\t\t\tenc = zapcore.NewConsoleEncoder(config.EncoderConfig)\n\t\tcase jsonEncoding:\n\t\t\tenc = zapcore.NewJSONEncoder(config.EncoderConfig)\n\t\t}\n\n\t\tswitch opts.kind {\n\t\tcase typeStd:\n\t\t\tswitch opts.path {\n\t\t\tcase \"\":\n\t\t\tcase \"stdout\", \"stderr\":\n\t\t\t\tconfig.OutputPaths = []string{opts.path}\n\t\t\tdefault:\n\t\t\t\tconfig.OutputPaths = []string{opts.path}\n\t\t\t}\n\n\t\t\tlogger, err := config.Build()\n\t\t\tif err != nil {\n\t\t\t\treturn nil, nil, err\n\t\t\t}\n\t\t\tcore = logger.Core()\n\t\tcase typeRing:\n\t\t\tring := opts.ring.SetEncoder(enc)\n\t\t\tcore = ring\n\t\tcase typeFile:\n\t\t\twriter, err := newFileWriteCloser(opts.path, opts.sessionKind)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, nil, errcode.ErrCode_TODO.Wrap(err)\n\t\t\t}\n\t\t\tw := zapcore.AddSync(writer)\n\t\t\tcore = zapcore.NewCore(enc, w, config.Level)\n\t\t\tcleanup = u.CombineFuncs(cleanup, func() { _ = writer.Close() })\n\t\tcase typeCustom:\n\t\t\tcore = opts.baseLogger.Core()\n\t\tdefault:\n\t\t\treturn nil, nil, fmt.Errorf(\"unknown logger type: %q\", opts.kind)\n\t\t}\n\n\t\tfilter, err := zapfilter.ParseRules(opts.filters)\n\t\tif err != nil {\n\t\t\treturn nil, nil, err\n\t\t}\n\t\tfiltered := zapfilter.NewFilteringCore(core, filter)\n\n\t\tif !withIPFS && zapfilter.CheckAnyLevel(zap.New(filtered).Named(\"ipfs\")) {\n\t\t\twithIPFS = true\n\t\t\tif zapfilter.CheckLevel(zap.New(filtered).Named(\"ipfs\"), zap.DebugLevel) {\n\t\t\t\twithIPFSDebug = true\n\t\t\t}\n\t\t}\n\n\t\tcores = append(cores, filtered)\n\t}\n\n\tif len(cores) == 0 {\n\t\treturn zap.NewNop(), cleanup, nil\n\t}\n\n\t// combine cores\n\ttee := zap.New(\n\t\tzapcore.NewTee(cores...),\n\t\tzap.AddCaller(),\n\t)\n\n\tif withIPFS {\n\t\tif withIPFSDebug {\n\t\t\tipfs_log.SetDebugLogging()\n\t\t}\n\t\tipfs_log.SetPrimaryCore(tee.Core())\n\t} else {\n\t\tipfs_log.SetPrimaryCore(zapcore.NewNopCore())\n\t}\n\n\tcleanup = u.CombineFuncs(cleanup, func() { _ = tee.Sync() })\n\n\treturn tee.Named(\"bty\"), cleanup, nil\n}\n"
  },
  {
    "path": "pkg/logutil/logutil_test.go",
    "content": "package logutil_test\n\nimport (\n\t\"bufio\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\t\"moul.io/u\"\n\t\"moul.io/zapring\"\n\n\t\"berty.tech/weshnet/v2/pkg/logutil\"\n)\n\nfunc TestTypeStd(t *testing.T) {\n\tif runtime.GOOS == \"windows\" {\n\t\tt.Skip(\"unittest not consistent on windows, skipping.\")\n\t}\n\n\tcloser, err := u.CaptureStdoutAndStderr()\n\trequire.NoError(t, err)\n\n\tlogger, cleanup, err := logutil.NewLogger(\n\t\tlogutil.NewStdStream(\"*\", \"light-console\", \"stdout\"),\n\t)\n\trequire.NoError(t, err)\n\tdefer cleanup()\n\n\tlogger.Info(\"hello world!\")\n\tlogger.Warn(\"hello world!\")\n\tlogger.Sync()\n\tlines := strings.Split(strings.TrimSpace(closer()), \"\\n\")\n\trequire.Equal(t, 2, len(lines))\n\trequire.Equal(t, \"INFO \\tbty               \\tlogutil/logutil_test.go:33\\thello world!\", lines[0])\n\trequire.Equal(t, \"WARN \\tbty               \\tlogutil/logutil_test.go:34\\thello world!\", lines[1])\n}\n\nfunc TestTypeRing(t *testing.T) {\n\tif runtime.GOOS == \"windows\" {\n\t\tt.Skip(\"unittest not consistent on windows, skipping.\")\n\t}\n\n\tcloser, err := u.CaptureStdoutAndStderr()\n\trequire.NoError(t, err)\n\n\tring := zapring.New(10 * 1024 * 1024) // 10MB ring-buffer\n\tdefer ring.Close()\n\n\tlogger, cleanup, err := logutil.NewLogger(\n\t\tlogutil.NewRingStream(\"*\", \"light-console\", ring),\n\t)\n\tdefer cleanup()\n\trequire.NoError(t, err)\n\n\tlogger.Info(\"hello world!\")\n\tlogger.Warn(\"hello world!\")\n\tlogger.Sync()\n\n\trequire.Empty(t, closer())\n\n\tr, w := io.Pipe()\n\tgo func() {\n\t\t_, err := ring.WriteTo(w)\n\t\trequire.True(t, err == nil || err == io.EOF)\n\t\tw.Close()\n\t}()\n\tscanner := bufio.NewScanner(r)\n\tscanner.Scan()\n\trequire.Equal(t, \"INFO \\tbty               \\tlogutil/logutil_test.go:59\\thello world!\", scanner.Text())\n\tscanner.Scan()\n\trequire.Equal(t, \"WARN \\tbty               \\tlogutil/logutil_test.go:60\\thello world!\", scanner.Text())\n}\n\nfunc TestTypeFile(t *testing.T) {\n\tt.Run(\"fullpath\", func(t *testing.T) {\n\t\tif runtime.GOOS == \"windows\" {\n\t\t\tt.Skip(\"unittest not consistent on windows, skipping.\")\n\t\t}\n\n\t\ttempdir, err := os.MkdirTemp(\"\", \"logutil-file\")\n\t\trequire.NoError(t, err)\n\n\t\tfilename := filepath.Join(tempdir, \"test.log\")\n\n\t\tcloser, err := u.CaptureStdoutAndStderr()\n\t\trequire.NoError(t, err)\n\n\t\tlogger, cleanup, err := logutil.NewLogger(\n\t\t\tlogutil.NewFileStream(\"*\", \"light-console\", filename, \"\"),\n\t\t)\n\t\trequire.NoError(t, err)\n\t\tdefer cleanup()\n\n\t\tlogger.Info(\"hello world!\")\n\t\tlogger.Warn(\"hello world!\")\n\t\tlogger.Sync()\n\n\t\trequire.Empty(t, closer())\n\n\t\tcontent, err := os.ReadFile(filename)\n\t\trequire.NoError(t, err)\n\t\tlines := strings.Split(string(content), \"\\n\")\n\t\trequire.Equal(t, 3, len(lines))\n\t\trequire.Equal(t, \"INFO \\tbty               \\tlogutil/logutil_test.go:98\\thello world!\", lines[0])\n\t\trequire.Equal(t, \"WARN \\tbty               \\tlogutil/logutil_test.go:99\\thello world!\", lines[1])\n\t\trequire.Equal(t, \"\", lines[2])\n\t})\n\n\tt.Run(\"pattern\", func(t *testing.T) {\n\t\tif runtime.GOOS == \"windows\" {\n\t\t\tt.Skip(\"unittest not consistent on windows, skipping.\")\n\t\t}\n\n\t\ttempdir, err := os.MkdirTemp(\"\", \"logutil-file\")\n\t\trequire.NoError(t, err)\n\n\t\tcloser, err := u.CaptureStdoutAndStderr()\n\t\trequire.NoError(t, err)\n\n\t\tlogger, cleanup, err := logutil.NewLogger(\n\t\t\tlogutil.NewFileStream(\"*\", \"light-console\", tempdir, \"just.a.test\"),\n\t\t)\n\t\trequire.NoError(t, err)\n\t\tdefer cleanup()\n\n\t\tlogger.Info(\"hello world!\")\n\t\tlogger.Warn(\"hello world!\")\n\t\tlogger.Sync()\n\n\t\trequire.Empty(t, closer())\n\n\t\tfiles, err := os.ReadDir(tempdir)\n\t\trequire.NoError(t, err)\n\t\trequire.Len(t, files, 1)\n\n\t\tcontent, err := os.ReadFile(filepath.Join(tempdir, files[0].Name()))\n\t\trequire.NoError(t, err)\n\t\tlines := strings.Split(string(content), \"\\n\")\n\t\trequire.Equal(t, 3, len(lines))\n\t\trequire.Equal(t, \"INFO \\tbty               \\tlogutil/logutil_test.go:130\\thello world!\", lines[0])\n\t\trequire.Equal(t, \"WARN \\tbty               \\tlogutil/logutil_test.go:131\\thello world!\", lines[1])\n\t\trequire.Equal(t, \"\", lines[2])\n\t})\n}\n\nfunc TestMultiple(t *testing.T) {\n\tif runtime.GOOS == \"windows\" {\n\t\tt.Skip(\"unittest not consistent on windows, skipping.\")\n\t}\n\n\ttempdir, err := os.MkdirTemp(\"\", \"logutil-file\")\n\trequire.NoError(t, err)\n\tdefer os.RemoveAll(tempdir)\n\n\t// ring\n\tring := zapring.New(10 * 1024 * 1024) // 10MB ring-buffer\n\tdefer ring.Close()\n\n\tcloser, err := u.CaptureStdoutAndStderr()\n\trequire.NoError(t, err)\n\n\tlogger, cleanup, err := logutil.NewLogger(\n\t\tlogutil.NewFileStream(\"*\", \"light-console\", filepath.Join(tempdir, \"test.log\"), \"\"),\n\t\tlogutil.NewRingStream(\"*\", \"light-console\", ring),\n\t\tlogutil.NewStdStream(\"*\", \"light-console\", \"stdout\"),\n\t)\n\trequire.NoError(t, err)\n\tdefer cleanup()\n\n\tlogger.Info(\"hello world!\")\n\tlogger.Warn(\"hello world!\")\n\tlogger.Sync()\n\n\t// std\n\t{\n\t\tlines := strings.Split(strings.TrimSpace(closer()), \"\\n\")\n\t\trequire.Equal(t, 2, len(lines))\n\t\trequire.Equal(t, \"INFO \\tbty               \\tlogutil/logutil_test.go:174\\thello world!\", lines[0])\n\t\trequire.Equal(t, \"WARN \\tbty               \\tlogutil/logutil_test.go:175\\thello world!\", lines[1])\n\t}\n\n\t// file\n\t{\n\t\tcontent, err := os.ReadFile(filepath.Join(tempdir, \"test.log\"))\n\t\trequire.NoError(t, err)\n\t\tlines := strings.Split(string(content), \"\\n\")\n\t\trequire.Equal(t, 3, len(lines))\n\t\trequire.Equal(t, \"INFO \\tbty               \\tlogutil/logutil_test.go:174\\thello world!\", lines[0])\n\t\trequire.Equal(t, \"WARN \\tbty               \\tlogutil/logutil_test.go:175\\thello world!\", lines[1])\n\t\trequire.Equal(t, \"\", lines[2])\n\t}\n\n\t// ring\n\t{\n\t\tr, w := io.Pipe()\n\t\tgo func() {\n\t\t\t_, err := ring.WriteTo(w)\n\t\t\trequire.True(t, err == nil || err == io.EOF)\n\t\t\tw.Close()\n\t\t}()\n\t\tscanner := bufio.NewScanner(r)\n\t\tscanner.Scan()\n\t\trequire.Equal(t, \"INFO \\tbty               \\tlogutil/logutil_test.go:174\\thello world!\", scanner.Text())\n\t\tscanner.Scan()\n\t\trequire.Equal(t, \"WARN \\tbty               \\tlogutil/logutil_test.go:175\\thello world!\", scanner.Text())\n\t}\n\n\t// FIXME: test that each logger can have its own format and filters\n}\n\n// FIXME: add unit test for NewCustomStream\n"
  },
  {
    "path": "pkg/logutil/private_field.go",
    "content": "package logutil\n\nimport (\n\tcrand \"crypto/rand\"\n\t\"crypto/sha256\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"sync\"\n\n\t\"go.uber.org/zap\"\n)\n\nvar (\n\tglobal *PrivateField\n\tmu     sync.RWMutex\n)\n\ntype PrivateField struct {\n\tNamespace []byte\n\tEnabled   bool\n}\n\nfunc (p *PrivateField) hash(value string) string {\n\thash := sha256.New()\n\tif _, err := hash.Write(p.Namespace); err != nil {\n\t\treturn \"unrepresentable\"\n\t}\n\n\tif _, err := hash.Write([]byte(value)); err != nil {\n\t\treturn \"unrepresentable\"\n\t}\n\n\thashed := hash.Sum(nil)\n\n\treturn hex.EncodeToString(hashed)\n}\n\nfunc (p *PrivateField) PrivateString(key string, value string) zap.Field {\n\tif p.Enabled {\n\t\treturn zap.String(key, p.hash(value))\n\t}\n\n\treturn zap.String(key, value)\n}\n\nfunc (p *PrivateField) PrivateStringer(key string, value fmt.Stringer) zap.Field {\n\tif p.Enabled {\n\t\treturn zap.String(key, p.hash(value.String()))\n\t}\n\n\treturn zap.Stringer(key, value)\n}\n\nfunc (p *PrivateField) PrivateStrings(key string, values []string) zap.Field {\n\tif p.Enabled {\n\t\tstrings := make([]string, len(values))\n\t\tfor i := range values {\n\t\t\tstrings[i] = p.hash(values[i])\n\t\t}\n\n\t\treturn zap.Strings(key, strings)\n\t}\n\n\treturn zap.Strings(key, values)\n}\n\nfunc (p *PrivateField) PrivateAny(key string, value any) zap.Field {\n\tif p.Enabled {\n\t\treturn zap.String(key, p.hash(fmt.Sprintf(\"%+v\", value)))\n\t}\n\n\treturn zap.Any(key, value)\n}\n\nfunc (p *PrivateField) PrivateBinary(key string, value []byte) zap.Field {\n\tif p.Enabled {\n\t\treturn zap.String(key, p.hash(hex.EncodeToString(value)))\n\t}\n\n\treturn zap.Binary(key, value)\n}\n\nfunc PrivateStrings(key string, value []string) zap.Field {\n\tmu.RLock()\n\tg := global\n\tmu.RUnlock()\n\n\treturn g.PrivateStrings(key, value)\n}\n\nfunc PrivateString(key string, value string) zap.Field {\n\tmu.RLock()\n\tg := global\n\tmu.RUnlock()\n\n\treturn g.PrivateString(key, value)\n}\n\nfunc PrivateStringer(key string, value fmt.Stringer) zap.Field {\n\tmu.RLock()\n\tg := global\n\tmu.RUnlock()\n\n\treturn g.PrivateStringer(key, value)\n}\n\nfunc PrivateAny(key string, value any) zap.Field {\n\tmu.RLock()\n\tg := global\n\tmu.RUnlock()\n\n\treturn g.PrivateAny(key, value)\n}\n\nfunc PrivateBinary(key string, value []byte) zap.Field {\n\tmu.RLock()\n\tg := global\n\tmu.RUnlock()\n\n\treturn g.PrivateBinary(key, value)\n}\n\nfunc SetGlobal(namespace []byte, enabled bool) {\n\tmu.Lock()\n\tglobal = &PrivateField{\n\t\tEnabled:   enabled,\n\t\tNamespace: namespace,\n\t}\n\tmu.Unlock()\n}\n\nfunc DisablePrivateFields() {\n\tSetGlobal(nil, false)\n}\n\nfunc init() { // nolint:gochecknoinits\n\tnamespace := make([]byte, 32)\n\t_, err := crand.Reader.Read(namespace)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tSetGlobal(namespace, true)\n}\n"
  },
  {
    "path": "pkg/logutil/stream.go",
    "content": "package logutil\n\nimport (\n\t\"go.uber.org/zap\"\n\t\"moul.io/zapring\"\n)\n\nconst (\n\ttypeStd    = \"std\"\n\ttypeRing   = \"ring\"\n\ttypeFile   = \"file\"\n\ttypeCustom = \"custom\"\n)\n\ntype Stream struct {\n\tkind        string\n\tfilters     string\n\tformat      string\n\tpath        string\n\tring        *zapring.Core\n\tsessionKind string\n\tbaseLogger  *zap.Logger\n}\n\nfunc NewStdStream(filters, format, path string) Stream {\n\treturn Stream{\n\t\tkind:    typeStd,\n\t\tfilters: filters,\n\t\tformat:  format,\n\t\tpath:    path,\n\t}\n}\n\nfunc NewRingStream(filters, format string, ring *zapring.Core) Stream {\n\treturn Stream{\n\t\tkind:    typeRing,\n\t\tfilters: filters,\n\t\tformat:  format,\n\t\tring:    ring,\n\t}\n}\n\n// NewFileStream creates a new file stream backed by Lumberjack with sane default values.\n//\n// Usually, Lumberjack is used as a rolling log file and is intended to be reused from a session to another,\n// In Berty, we want one file per session named with the start time instead of the rotation time.\n//\n// If the provided path is a directory, it will create files in that directory with the following pattern:\n// `<path>/<session-kind>-<start-time>.log`.\n//\n// If the provided path is a path finishing with \".log\", then, the path will be taken as it,\n// instead of creating a new file, it will append new lines to the existing one;\n// this can be particularly useful to keep a `tail -f` running.\nfunc NewFileStream(filters, format, path, sessionKind string) Stream {\n\treturn Stream{\n\t\tkind:        typeFile,\n\t\tfilters:     filters,\n\t\tformat:      format,\n\t\tpath:        path,\n\t\tsessionKind: sessionKind,\n\t}\n}\n\nfunc NewCustomStream(filters string, logger *zap.Logger) Stream {\n\treturn Stream{\n\t\tkind:       typeCustom,\n\t\tfilters:    filters,\n\t\tbaseLogger: logger,\n\t}\n}\n"
  },
  {
    "path": "pkg/multipeer-connectivity-driver/bridge_darwin.go",
    "content": "//go:build darwin && cgo && !catalyst && !noproximitytransport\n\npackage mc\n\nimport (\n\t\"go.uber.org/zap\"\n\n\tnative \"berty.tech/weshnet/v2/pkg/multipeer-connectivity-driver/driver\"\n\tproximity \"berty.tech/weshnet/v2/pkg/proximitytransport\"\n)\n\nconst Supported = true\n\ntype Driver struct {\n\tprotocolCode int\n\tprotocolName string\n\tdefaultAddr  string\n}\n\n// Driver is a proximity.ProximityDriver\nvar _ proximity.ProximityDriver = (*Driver)(nil)\n\nfunc NewDriver(logger *zap.Logger) proximity.ProximityDriver {\n\tif logger == nil {\n\t\tlogger = zap.NewNop()\n\t} else {\n\t\tlogger = logger.Named(\"MC\")\n\t\tlogger.Debug(\"NewDriver()\")\n\t\tnative.MCUseExternalLogger()\n\t}\n\tnative.Logger = logger\n\tnative.ProtocolName = ProtocolName\n\n\treturn &Driver{\n\t\tprotocolCode: ProtocolCode,\n\t\tprotocolName: ProtocolName,\n\t\tdefaultAddr:  DefaultAddr,\n\t}\n}\n\nfunc (d *Driver) Start(localPID string) {\n\tnative.Start(localPID)\n}\n\nfunc (d *Driver) Stop() {\n\tnative.Stop()\n}\n\nfunc (d *Driver) DialPeer(remotePID string) bool {\n\treturn native.DialPeer(remotePID)\n}\n\nfunc (d *Driver) SendToPeer(remotePID string, payload []byte) bool {\n\treturn native.SendToPeer(remotePID, payload)\n}\n\nfunc (d *Driver) CloseConnWithPeer(remotePID string) {\n\tnative.CloseConnWithPeer(remotePID)\n}\n\nfunc (d *Driver) ProtocolCode() int {\n\treturn d.protocolCode\n}\n\nfunc (d *Driver) ProtocolName() string {\n\treturn d.protocolName\n}\n\nfunc (d *Driver) DefaultAddr() string {\n\treturn d.defaultAddr\n}\n"
  },
  {
    "path": "pkg/multipeer-connectivity-driver/bridge_unsupported.go",
    "content": "//go:build !darwin || (darwin && !cgo) || catalyst || noproximitytransport\n\npackage mc\n\nimport (\n\t\"go.uber.org/zap\"\n\n\tproximity \"berty.tech/weshnet/v2/pkg/proximitytransport\"\n)\n\nconst Supported = false\n\n// Noop implementation for platform that are not Darwin\n\nfunc NewDriver(logger *zap.Logger) proximity.ProximityDriver {\n\tlogger = logger.Named(\"MC\")\n\tlogger.Info(\"NewDriver(): incompatible system\")\n\n\treturn proximity.NewNoopProximityDriver(ProtocolCode, ProtocolName, DefaultAddr)\n}\n"
  },
  {
    "path": "pkg/multipeer-connectivity-driver/const.go",
    "content": "package mc\n\nconst (\n\tDefaultAddr  = \"/mc/Qmeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\"\n\tProtocolCode = 0x0043\n\tProtocolName = \"mc\"\n)\n"
  },
  {
    "path": "pkg/multipeer-connectivity-driver/driver/Logger.h",
    "content": "//\n//  Logger.h\n//  BertyBridgeDemo\n//\n//  Created by Rémi BARBERO on 08/12/2021.\n//\n\n#import <Foundation/Foundation.h>\n#import <os/log.h>\n\nNS_ASSUME_NONNULL_BEGIN\n\n#define SENSITIVE_MASK @\"####\"\n\n// Log levels\ntypedef NS_ENUM(uint8_t, level) {\n    Debug,\n    Info,\n    Warn,\n    Error,\n};\n\n@interface MC_Logger : NSObject\n\n@property (nonatomic, strong, nonnull) os_log_t logger;\n@property (readwrite) BOOL showSensitiveData;\n@property (readwrite) BOOL useExternalLogger;\n\n- (instancetype __nonnull)initLocalLoggerWithSubSystem:(const char *)subSystem andCategorie:(const char*)categorie showSensitiveData:(BOOL)showSensitiveData;\n- (instancetype __nonnull)initWithExternalLoggerAndShowSensitiveData:(BOOL)showSensitiveData;\n- (void)log:(enum level)level withFormat:(NSString *__nonnull)format withArgs:(va_list)args;\n- (void)d:(NSString *__nonnull)format, ...;\n- (void)i:(NSString *__nonnull)format, ...;\n- (void)e:(NSString *__nonnull)format, ...;\n- (BOOL)showSensitiveData;\n- (BOOL)useExternalLogger;\n- (NSString *__nonnull)SensitiveNSObject:(id __nonnull)data;\n- (NSString *__nonnull)SensitiveString:(const char *)data;\n\n@end\n\n@compatibility_alias Logger MC_Logger;\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "pkg/multipeer-connectivity-driver/driver/Logger.m",
    "content": "// +build darwin,!noproximitytransport\n//\n//  Logger.m\n//  BertyBridgeDemo\n//\n//  Created by Rémi BARBERO on 08/12/2021.\n//\n\n#import <os/log.h>\n#import \"Logger.h\"\n#import \"mc-driver.h\"\n\n@implementation Logger\n\n- (instancetype __nonnull)initLocalLoggerWithSubSystem:(const char *)subSystem andCategorie:(const char*)categorie showSensitiveData:(BOOL)showSensitiveData {\n    self = [super init];\n\n    if (self) {\n        _logger = os_log_create(subSystem, categorie);\n        _useExternalLogger = FALSE;\n        _showSensitiveData = showSensitiveData;\n    }\n\n    return self;\n}\n\n- (instancetype __nonnull)initWithExternalLoggerAndShowSensitiveData:(BOOL)showSensitiveData {\n    self = [super init];\n\n    if (self) {\n        _logger = nil;\n        _useExternalLogger = TRUE;\n        _showSensitiveData = showSensitiveData;\n    }\n\n    return self;\n}\n\n- (void)log:(enum level)level withFormat:(NSString *__nonnull)format withArgs:(va_list)args {\n    NSString *message = [[NSString alloc] initWithFormat:format arguments:args];\n\n    if (self.useExternalLogger) {\n        MCBridgeLog(level, message);\n    } else {\n        if (self.logger == nil) {\n            NSLog(@\"log error: logger is not set\");\n        } else {\n            uint8_t osLevel;\n            switch (level) {\n                case Debug:\n                    osLevel = OS_LOG_TYPE_DEBUG;\n                    break ;\n                case Info:\n                    osLevel = OS_LOG_TYPE_INFO;\n                    break ;\n                case Error:\n                    osLevel = OS_LOG_TYPE_ERROR;\n                    break ;\n                default:\n                    osLevel = OS_LOG_TYPE_DEFAULT;\n                    break ;\n            }\n\n            os_log_with_type(self.logger, osLevel, \"%@\", message);\n        }\n    }\n\n    [message release];\n}\n\n- (void)d:(NSString *__nonnull)format, ... {\n    va_list args;\n    va_start(args, format);\n    [self log:Debug withFormat:format withArgs:args];\n    va_end(args);\n}\n\n- (void)i:(NSString *__nonnull)format, ... {\n    va_list args;\n    va_start(args, format);\n    [self log:Info withFormat:format withArgs:args];\n    va_end(args);\n}\n\n- (void)e:(NSString *__nonnull)format, ... {\n    va_list args;\n    va_start(args, format);\n    [self log:Error withFormat:format withArgs:args];\n    va_end(args);\n}\n\n- (NSString *__nonnull)SensitiveNSObject:(id __nonnull)data {\n    if (self.showSensitiveData) {\n        return [NSString stringWithFormat:@\"%@\", data];\n    } else {\n        return SENSITIVE_MASK;\n    }\n}\n\n- (NSString *__nonnull)SensitiveString:(const char *)data {\n    if (data == nil) {\n        return @\"\";\n    }\n\n    if (self.showSensitiveData) {\n        return [NSString stringWithFormat:@\"%s\", data];\n    } else {\n        return SENSITIVE_MASK;\n    }\n}\n\n@end\n"
  },
  {
    "path": "pkg/multipeer-connectivity-driver/driver/MCManager.h",
    "content": "//\n//  MCManager.h\n//  driver\n//\n//  Created by Rémi BARBERO on 31/03/2020.\n//  Copyright © 2020 Rémi BARBERO. All rights reserved.\n//\n\n#import <Foundation/Foundation.h>\n#import <MultipeerConnectivity/MultipeerConnectivity.h>\n\n#import \"Logger.h\"\n\nNS_ASSUME_NONNULL_BEGIN\n\n@interface MCManager : NSObject <MCSessionDelegate, MCNearbyServiceAdvertiserDelegate, MCNearbyServiceBrowserDelegate>\n\n@property (nonatomic, strong) MCSession *mSession;\n@property (nonatomic, strong, nullable) MCNearbyServiceAdvertiser *mServiceAdvertiser;\n@property (nonatomic, strong, nullable) MCNearbyServiceBrowser *mServiceBrowser;\n@property (nonatomic, strong) MCPeerID *mPeerID;\n@property (nonatomic, strong, nullable) Logger *logger;\n\n- (MCPeerID *)getMCPeerID:(NSString *)peerID;\n- (id)init:(NSString *)peerID useExternalLogger:(BOOL)useExternalLogger;\n- (int)startServiceAdvertiser;\n- (int)startServiceBrowser;\n- (void)stopServiceAdvertiser;\n- (void)stopServiceBrowser;\n- (void)closeSessions;\n- (int)sendToPeer:(NSString *)peerID data:(NSData *)data;\n- (MCPeerID *)getPeer:(NSString *)peerID;\n\n@end\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "pkg/multipeer-connectivity-driver/driver/MCManager.m",
    "content": "// +build darwin,!noproximitytransport\n\n#import <MultipeerConnectivity/MultipeerConnectivity.h>\n#import \"MCManager.h\"\n#import \"mc-driver.h\"\n\nNSString *BERTY_DRIVER_MC = @\"berty-mc\";\n\n@implementation MCManager\n\n// MCPeerID must be unique and stable over time so we need to archive it after\n// the first time it's created.\n// https://developer.apple.com/documentation/multipeerconnectivity/mcpeerid\n- (MCPeerID *)getMCPeerID:(NSString *)appPID {\n    NSString *kAppPID = @\"berty-peerID\";\n    NSString *kPIDData = @\"berty-PIDData\";\n\n    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];\n    NSString *oldAppPID = [defaults stringForKey:kAppPID];\n    MCPeerID *peerID;\n    NSData *peerIDData;\n    NSError *error;\n\n    if ([oldAppPID isEqualToString:appPID]) {\n        peerIDData = [defaults dataForKey:kPIDData];\n        if ((peerID = [NSKeyedUnarchiver unarchivedObjectOfClass:[MCPeerID class] fromData:peerIDData error:&error])) {\n            return (peerID);\n        }\n        [self.logger d:@\"getMCPeerID unarchive error: %@\", error];\n    }\n    peerID = [[MCPeerID alloc] initWithDisplayName:appPID];\n    if ((peerIDData = [NSKeyedArchiver archivedDataWithRootObject:peerID requiringSecureCoding:true error:&error])) {\n        [defaults setObject:peerIDData forKey:kPIDData];\n        [defaults setObject:appPID forKey:kAppPID];\n        [defaults synchronize];\n    } else {\n        [self.logger d:@\"getMCPeerID archive error: %@\", error];\n    }\n    return (peerID);\n}\n\n- (id)init:(NSString *)peerID useExternalLogger:(BOOL)useExternalLogger {\n    if (self = [super init]) {\n        _mPeerID = [[MCPeerID alloc] initWithDisplayName:peerID];\n\n        BOOL showSensitiveData = FALSE;\n        if (useExternalLogger) {\n            _logger = [[Logger alloc] initWithExternalLoggerAndShowSensitiveData:showSensitiveData];\n        } else {\n            _logger = [[Logger alloc] initLocalLoggerWithSubSystem:\"tech.berty.bty\" andCategorie:\"MC\" showSensitiveData:showSensitiveData];\n        }\n\n        if (!(self.mSession = [[MCSession alloc] initWithPeer:self.mPeerID securityIdentity:nil encryptionPreference:MCEncryptionRequired])) {\n            [self.logger d:@\"MCSession init failed\"];\n            return (self = nil);\n        }\n        self.mSession.delegate = self;\n    }\n    return (self);\n}\n\n- (int)startServiceAdvertiser {\n    if (!(self.mServiceAdvertiser = [[MCNearbyServiceAdvertiser alloc] initWithPeer:self.mPeerID discoveryInfo:nil serviceType:BERTY_DRIVER_MC])) {\n        [self.logger d:@\"MCNearbyServiceAdvertiser init failed\"];\n        return (0);\n    }\n    self.mServiceAdvertiser.delegate = self;\n    [self.mServiceAdvertiser startAdvertisingPeer];\n    return (1);\n}\n\n- (int)startServiceBrowser {\n    if (!(self.mServiceBrowser = [[MCNearbyServiceBrowser alloc] initWithPeer:self.mPeerID serviceType:BERTY_DRIVER_MC])) {\n        [self.logger d:@\"MCNearbyServiceBrowser init failed\"];\n        return (0);\n    }\n    self.mServiceBrowser.delegate = self;\n    [self.mServiceBrowser startBrowsingForPeers];\n    return (1);\n}\n\n- (void)stopServiceAdvertiser {\n    [self.mServiceAdvertiser stopAdvertisingPeer];\n    self.mServiceAdvertiser = nil;\n}\n\n- (void)stopServiceBrowser {\n    [self.mServiceBrowser stopBrowsingForPeers];\n    self.mServiceBrowser = nil;\n}\n\n- (void)closeSessions {\n    [self.mSession disconnect];\n}\n\n- (int)sendToPeer: (NSString *)peerID data:(NSData *)data {\n    NSError *error = nil;\n    MCPeerID *peer;\n    if ((peer = [self getPeer:peerID])) {\n        NSArray *array = @[peer];\n        if ([self.mSession sendData:data toPeers:array withMode:MCSessionSendDataReliable error:&error]) {\n            return (1);\n        }\n        NSString *description = [error localizedDescription];\n        NSString *reason = [error localizedFailureReason] ?\n            [error localizedFailureReason] :\n            NSLocalizedString(@\"Unknown reason\", nil);\n        [self.logger d:@\"sendToPeer error: %@: %@\", description, reason];\n    }\n    return (0);\n}\n\n- (MCPeerID *)getPeer:(NSString *)peerID {\n    NSArray<MCPeerID *> *peers = [self.mSession connectedPeers];\n    for (MCPeerID *peer in peers) {\n        if ([[peer displayName] isEqualToString:peerID]) {\n            return (peer);\n        }\n    }\n    return (nil);\n}\n\n/*\n                                MCSessionDelegate\n */\n\n- (void)session:(MCSession *)session peer:(MCPeerID *)peerID didChangeState:(MCSessionState)state{\n    switch (state) {\n    case MCSessionStateConnecting:\n            [self.logger d:@\"Connecting: %@\", [self.logger SensitiveNSObject:[peerID displayName]]];\n        break;\n    case MCSessionStateConnected:\n        [self.logger i:@\"Connected: %@\", [self.logger SensitiveNSObject:[peerID displayName]]];\n        BridgeHandleFoundPeer([peerID displayName]);\n        break;\n    case MCSessionStateNotConnected:\n        [self.logger i:@\"Not connected: %@\", [self.logger SensitiveNSObject:[peerID displayName]]];\n\t\tBridgeHandleLostPeer([peerID displayName]);\n        break;\n    }\n}\n\n- (void)session:(MCSession *)session didReceiveData:(NSData *)data fromPeer:(MCPeerID *)peerID{\n    BridgeReceiveFromPeer([peerID displayName], data);\n}\n\n\n- (void)session:(MCSession *)session didStartReceivingResourceWithName:(NSString *)resourceName fromPeer:(MCPeerID *)peerID withProgress:(NSProgress *)progress{\n\n}\n\n\n- (void)session:(MCSession *)session didFinishReceivingResourceWithName:(NSString *)resourceName fromPeer:(MCPeerID *)peerID atURL:(NSURL *)localURL withError:(NSError *)error{\n\n}\n\n\n- (void)session:(MCSession *)session didReceiveStream:(NSInputStream *)stream withName:(NSString *)streamName fromPeer:(MCPeerID *)peerID{\n\n}\n\n/*\n                                MCNearbyServiceAdvertiserDelegate\n */\n\n- (void)advertiser:(MCNearbyServiceAdvertiser *)advertiser didNotStartAdvertisingPeer:(NSError *)error {\n    [self.logger d:@\"didNotStartAdvertisingPeer: %@\", error];\n}\n\n- (void)advertiser:(MCNearbyServiceAdvertiser *)advertiser didReceiveInvitationFromPeer:(MCPeerID *)peerID withContext:(NSData *)context invitationHandler:(void (^)(BOOL, MCSession * _Nullable))invitationHandler {\n    [self.logger d:@\"didReceiveInvationFromPeer: %@\", [self.logger SensitiveNSObject:[peerID displayName]]];\n    invitationHandler(true, self.mSession);\n}\n\n/*\n                                MCNearbyServiceBrowserDelegate\n */\n\n- (void)browser:(MCNearbyServiceBrowser *)browser lostPeer:(MCPeerID *)peerID {\n    [self.logger d:@\"lostPeer: %@\", [self.logger SensitiveNSObject:[peerID displayName]]];\n}\n\n- (void)browser:(MCNearbyServiceBrowser *)browser didNotStartBrowsingForPeers:(NSError *)error {\n    [self.logger d:@\"didNotStartBrowsingForPeers: %@\", error];\n}\n\n- (void)browser:(MCNearbyServiceBrowser *)browser foundPeer:(MCPeerID *)peerID withDiscoveryInfo:(NSDictionary<NSString *,NSString *> *)info {\n    [self.logger d:@\"foundPeer: %@\", [self.logger SensitiveNSObject:[peerID displayName]]];\n    [browser invitePeer:peerID toSession:self.mSession withContext:nil timeout:10];\n}\n\n@end\n"
  },
  {
    "path": "pkg/multipeer-connectivity-driver/driver/cgo_bridge.go",
    "content": "//go:build darwin && cgo && !catalyst && !noproximitytransport\n// +build darwin,cgo,!catalyst,!noproximitytransport\n\npackage driver\n\n/*\n#cgo CFLAGS: -x objective-c\n#cgo darwin LDFLAGS: -framework Foundation -framework MultipeerConnectivity\n#include <stdlib.h>\n#include \"mc-driver.h\"\n*/\nimport \"C\"\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"unsafe\"\n\n\t\"go.uber.org/zap\"\n\n\tproximity \"berty.tech/weshnet/v2/pkg/proximitytransport\"\n)\n\nvar (\n\tLogger       *zap.Logger\n\tProtocolName string\n)\n\n//export MCHandleFoundPeer\nfunc MCHandleFoundPeer(remotePID *C.char) int {\n\tgoPID := C.GoString(remotePID)\n\n\tproximity.TransportMapMutex.RLock()\n\tt, ok := proximity.TransportMap[ProtocolName]\n\tproximity.TransportMapMutex.RUnlock()\n\tif !ok {\n\t\treturn 0\n\t}\n\tif t.HandleFoundPeer(goPID) {\n\t\treturn 1\n\t}\n\treturn 0\n}\n\n//export MCHandleLostPeer\nfunc MCHandleLostPeer(remotePID *C.char) {\n\tgoPID := C.GoString(remotePID)\n\n\tproximity.TransportMapMutex.RLock()\n\tt, ok := proximity.TransportMap[ProtocolName]\n\tproximity.TransportMapMutex.RUnlock()\n\tif !ok {\n\t\treturn\n\t}\n\tt.HandleLostPeer(goPID)\n}\n\n//export MCReceiveFromPeer\nfunc MCReceiveFromPeer(remotePID *C.char, payload unsafe.Pointer, length C.int) {\n\tgoPID := C.GoString(remotePID)\n\tgoPayload := C.GoBytes(payload, length)\n\n\tproximity.TransportMapMutex.RLock()\n\tt, ok := proximity.TransportMap[ProtocolName]\n\tproximity.TransportMapMutex.RUnlock()\n\tif !ok {\n\t\treturn\n\t}\n\tt.ReceiveFromPeer(goPID, goPayload)\n}\n\n//export MCLog\nfunc MCLog(level C.enum_level, message *C.char) { //nolint:golint\n\tif Logger == nil {\n\t\tfmt.Fprintf(os.Stderr, \"logger not found\\n\")\n\t\treturn\n\t}\n\n\tgoMessage := C.GoString(message)\n\tswitch level {\n\tcase C.Debug:\n\t\tLogger.Debug(goMessage)\n\tcase C.Info:\n\t\tLogger.Info(goMessage)\n\tcase C.Warn:\n\t\tLogger.Warn(goMessage)\n\tcase C.Error:\n\t\tLogger.Error(goMessage)\n\t}\n}\n\nfunc Start(localPID string) {\n\tcPID := C.CString(localPID)\n\tdefer C.free(unsafe.Pointer(cPID))\n\n\tC.StartMCDriver(cPID)\n}\n\nfunc Stop() {\n\tC.StopMCDriver()\n}\n\nfunc DialPeer(remotePID string) bool {\n\tcPID := C.CString(remotePID)\n\tdefer C.free(unsafe.Pointer(cPID))\n\n\treturn C.DialPeer(cPID) == 1\n}\n\nfunc SendToPeer(remotePID string, payload []byte) bool {\n\tcPID := C.CString(remotePID)\n\tdefer C.free(unsafe.Pointer(cPID))\n\tcPayload := C.CBytes(payload)\n\tdefer C.free(cPayload)\n\n\treturn C.SendToPeer(cPID, cPayload, C.int(len(payload))) == 1\n}\n\nfunc CloseConnWithPeer(remotePID string) {\n\tcPID := C.CString(remotePID)\n\tdefer C.free(unsafe.Pointer(cPID))\n\n\tC.CloseConnWithPeer(cPID)\n}\n\nfunc MCUseExternalLogger() {\n\tC.MCUseExternalLogger()\n}\n"
  },
  {
    "path": "pkg/multipeer-connectivity-driver/driver/mc-driver.h",
    "content": "//\n//  mc-driver.h\n//  driver\n//\n//  Created by Rémi BARBERO on 30/03/2020.\n//  Copyright © 2020 Rémi BARBERO. All rights reserved.\n//\n\n#import <Foundation/Foundation.h>\n#import <os/log.h>\n\n#import \"Logger.h\"\n\nvoid StartMCDriver(char *localPId);\nvoid StopMCDriver(void);\nint SendToPeer(char *remotePID, void *payload, int length);\nint DialPeer(char *remotePID);\nvoid CloseConnWithPeer(char *remotePID);\nint BridgeHandleFoundPeer(NSString *remotePID);\nvoid BridgeHandleLostPeer(NSString *remotePID);\nvoid BridgeReceiveFromPeer(NSString *remotePID, NSData *payload);\nvoid MCBridgeLog(enum level level, NSString *message);\nvoid MCUseExternalLogger(void);\n"
  },
  {
    "path": "pkg/multipeer-connectivity-driver/driver/mc-driver.m",
    "content": "// +build darwin,!noproximitytransport\n\n#import <MultipeerConnectivity/MultipeerConnectivity.h>\n#import \"mc-driver.h\"\n#import \"MCManager.h\"\n\n// This functions are Go functions so they aren't defined here\nextern int MCHandleFoundPeer(char *);\nextern void MCHandleLostPeer(char *);\nextern void MCReceiveFromPeer(char *, void *, unsigned long);\nextern void MCLog(enum level level, const char *message);\n\nint driverStarted = 0;\nBOOL gMCUseExternalLogger = FALSE;\n\n// MCManager must be unique\nstatic MCManager *gMCManager = nil;\nMCManager* getMCManager(NSString *peerID) {\n    static dispatch_once_t onceToken;\n    dispatch_once(&onceToken, ^{\n\t\tNSLog(@\"init MCManager\");\n        gMCManager = [[MCManager alloc] init:peerID useExternalLogger:gMCUseExternalLogger];\n    });\n    return gMCManager;\n}\n\nvoid StartMCDriver(char *localPID) {\n    if (!driverStarted) {\n\t\tNSLog(@\"StartMCDriver()\");\n        NSString *cPID = [[NSString alloc] initWithUTF8String:localPID];\n        if (!getMCManager(cPID)) {\n           NSLog(@\"StartMCDriver failed\");\n            return ;\n        }\n        [gMCManager startServiceAdvertiser];\n        [gMCManager startServiceBrowser];\n        driverStarted = 1;\n    }\n}\n\nvoid StopMCDriver() {\n    if (driverStarted) {\n\t\tNSLog(@\"StopMCDriver()\");\n        [gMCManager stopServiceAdvertiser];\n        [gMCManager stopServiceBrowser];\n        [gMCManager closeSessions];\n        driverStarted = 0;\n    }\n}\n\nint SendToPeer(char *remotePID, void *payload, int length) {\n    if (driverStarted) {\n\t\tNSString *cPID = [[NSString alloc] initWithUTF8String:remotePID];\n\t\tNSData *cPayload = [[NSData alloc] initWithBytes:payload length:length];\n\t\treturn ([gMCManager sendToPeer:cPID data:cPayload]);\n\t}\n\treturn (0);\n}\n\nint DialPeer(char *remotePID) {\n    NSString *cPID = [[NSString alloc] initWithUTF8String:remotePID];\n    if (!driverStarted || ![gMCManager getPeer:cPID]) {\n        return (0);\n    }\n\treturn (1);\n}\n\n// nothing to do because API doesn't provide any functions\nvoid CloseConnWithPeer(char *peerID) {\n}\n\n// Use MCBridgeLog to write logs to the external logger\nvoid MCUseExternalLogger(void) {\n    gMCUseExternalLogger = TRUE;\n}\n\nint BridgeHandleFoundPeer(NSString *remotePID) {\n    char *cPID = (char *)[remotePID UTF8String];\n    if (MCHandleFoundPeer(cPID)) {\n        return (1);\n    }\n    return (0);\n}\n\nvoid BridgeHandleLostPeer(NSString *remotePID) {\n    char *cPID = (char *)[remotePID UTF8String];\n    MCHandleLostPeer(cPID);\n}\n\nvoid BridgeReceiveFromPeer(NSString *remotePID, NSData *payload) {\n    char *cPID = (char *)[remotePID UTF8String];\n    char *cPayload = (char *)[payload bytes];\n    int length = (int)[payload length];\n    MCReceiveFromPeer(cPID, cPayload, length);\n}\n\n// Write logs to the external logger\nvoid MCBridgeLog(enum level level, NSString *message) {\n    char *cMessage = (char *)[message UTF8String];\n    MCLog(level, cMessage);\n}\n"
  },
  {
    "path": "pkg/multipeer-connectivity-driver/example_test.go",
    "content": "package mc_test\n"
  },
  {
    "path": "pkg/multipeer-connectivity-driver/init.go",
    "content": "package mc\n\nimport (\n\tma \"github.com/multiformats/go-multiaddr\"\n)\n\n// Add MC to the list of libp2p's multiaddr protocols\n// FIXME: remove this init\nfunc init() { // nolint:gochecknoinits\n\terr := ma.AddProtocol(newProtocol())\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n"
  },
  {
    "path": "pkg/multipeer-connectivity-driver/multiaddr.go",
    "content": "package mc\n\nimport (\n\tpeer \"github.com/libp2p/go-libp2p/core/peer\"\n\tma \"github.com/multiformats/go-multiaddr\"\n)\n\nfunc newProtocol() ma.Protocol {\n\ttranscoderMC := ma.NewTranscoderFromFunctions(mcStB, mcBtS, mcVal)\n\treturn ma.Protocol{\n\t\tName:       ProtocolName,\n\t\tCode:       ProtocolCode,\n\t\tVCode:      ma.CodeToVarint(ProtocolCode),\n\t\tSize:       -1,\n\t\tPath:       false,\n\t\tTranscoder: transcoderMC,\n\t}\n}\n\nfunc mcStB(s string) ([]byte, error) {\n\t_, err := peer.Decode(s)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn []byte(s), nil\n}\n\nfunc mcBtS(b []byte) (string, error) {\n\t_, err := peer.Decode(string(b))\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn string(b), nil\n}\n\nfunc mcVal(b []byte) error {\n\t_, err := peer.Decode(string(b))\n\treturn err\n}\n"
  },
  {
    "path": "pkg/netmanager/connectivity.go",
    "content": "package netmanager\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\ntype ConnectivityState int\n\nconst (\n\tConnectivityStateUnknown ConnectivityState = iota\n\tConnectivityStateOff\n\tConnectivityStateOn\n)\n\nfunc (cs ConnectivityState) String() string {\n\tswitch cs {\n\tcase ConnectivityStateUnknown:\n\t\treturn \"unknown\"\n\tcase ConnectivityStateOff:\n\t\treturn \"off\"\n\tcase ConnectivityStateOn:\n\t\treturn \"on\"\n\tdefault:\n\t\treturn \"error\"\n\t}\n}\n\nfunc ParseConnectivityState(s string) (ConnectivityState, error) {\n\tswitch strings.ToLower(s) {\n\tcase \"unknown\":\n\t\treturn ConnectivityStateUnknown, nil\n\tcase \"off\":\n\t\treturn ConnectivityStateOff, nil\n\tcase \"on\":\n\t\treturn ConnectivityStateOn, nil\n\tdefault:\n\t\treturn ConnectivityStateUnknown, fmt.Errorf(\"invalid connectivity state (unknown/off/on): %q\", s)\n\t}\n}\n\ntype ConnectivityNetType int\n\nconst (\n\tConnectivityNetUnknown ConnectivityNetType = iota\n\tConnectivityNetNone\n\tConnectivityNetWifi\n\tConnectivityNetEthernet\n\tConnectivityNetCellular\n)\n\nfunc (cnt ConnectivityNetType) String() string {\n\tswitch cnt {\n\tcase ConnectivityNetUnknown:\n\t\treturn \"unknown\"\n\tcase ConnectivityNetNone:\n\t\treturn \"none\"\n\tcase ConnectivityNetWifi:\n\t\treturn \"wifi\"\n\tcase ConnectivityNetEthernet:\n\t\treturn \"ethernet\"\n\tcase ConnectivityNetCellular:\n\t\treturn \"cellular\"\n\tdefault:\n\t\treturn \"error\"\n\t}\n}\n\nfunc ParseConnectivityNetType(s string) (ConnectivityNetType, error) {\n\tswitch strings.ToLower(s) {\n\tcase \"unknown\":\n\t\treturn ConnectivityNetUnknown, nil\n\tcase \"none\":\n\t\treturn ConnectivityNetNone, nil\n\tcase \"wifi\":\n\t\treturn ConnectivityNetWifi, nil\n\tcase \"ethernet\":\n\t\treturn ConnectivityNetEthernet, nil\n\tcase \"cellular\":\n\t\treturn ConnectivityNetCellular, nil\n\tdefault:\n\t\treturn ConnectivityNetUnknown, fmt.Errorf(\"invalid connectivity net type (unknown/none/wifi/ethernet/cellular): %q\", s)\n\t}\n}\n\ntype ConnectivityCellularType int\n\nconst (\n\tConnectivityCellularUnknown ConnectivityCellularType = iota\n\tConnectivityCellularNone\n\tConnectivityCellular2G\n\tConnectivityCellular3G\n\tConnectivityCellular4G\n\tConnectivityCellular5G\n)\n\nfunc (cct ConnectivityCellularType) String() string {\n\tswitch cct {\n\tcase ConnectivityCellularUnknown:\n\t\treturn \"unknown\"\n\tcase ConnectivityCellularNone:\n\t\treturn \"none\"\n\tcase ConnectivityCellular2G:\n\t\treturn \"2G\"\n\tcase ConnectivityCellular3G:\n\t\treturn \"3G\"\n\tcase ConnectivityCellular4G:\n\t\treturn \"4G\"\n\tcase ConnectivityCellular5G:\n\t\treturn \"5G\"\n\tdefault:\n\t\treturn \"error\"\n\t}\n}\n\nfunc ParseConnectivityCellularType(s string) (ConnectivityCellularType, error) {\n\tswitch strings.ToLower(s) {\n\tcase \"unknown\":\n\t\treturn ConnectivityCellularUnknown, nil\n\tcase \"none\":\n\t\treturn ConnectivityCellularNone, nil\n\tcase \"2g\":\n\t\treturn ConnectivityCellular2G, nil\n\tcase \"3g\":\n\t\treturn ConnectivityCellular3G, nil\n\tcase \"4g\":\n\t\treturn ConnectivityCellular4G, nil\n\tcase \"5g\":\n\t\treturn ConnectivityCellular5G, nil\n\tdefault:\n\t\treturn ConnectivityCellularUnknown, fmt.Errorf(\"invalid connectivity cellular type (unknown/none/2g/3g/4g/5g): %q\", s)\n\t}\n}\n\ntype ConnectivityInfo struct {\n\t// False when the device is not connected to a network.\n\tState ConnectivityState\n\n\t// True when the device is connected to a metered network.\n\tMetering ConnectivityState\n\n\t// True when the device is connected to a bluetooth network.\n\tBluetooth ConnectivityState\n\n\t// The type of the network the device is connected to: wifi/ethernet/cellular.\n\tNetType ConnectivityNetType\n\n\t// If the device is connected to a cellular network:\n\t// The type of the cellular network the device is connected to: 2G/3G/4G/5G.\n\tCellularType ConnectivityCellularType\n}\n\nfunc (ci ConnectivityInfo) String() string {\n\treturn fmt.Sprint(\"ConnectivityInfo{ \",\n\t\t\"State: \", ci.State.String(), \", \",\n\t\t\"Metering: \", ci.Metering.String(), \", \",\n\t\t\"Bluetooth: \", ci.Bluetooth.String(), \", \",\n\t\t\"NetType: \", ci.NetType.String(), \", \",\n\t\t\"CellularType: \", ci.CellularType.String(),\n\t\t\" }\")\n}\n"
  },
  {
    "path": "pkg/netmanager/netmanager.go",
    "content": "package netmanager\n\nimport (\n\t\"context\"\n\t\"sync\"\n\n\t\"berty.tech/weshnet/v2/internal/notify\"\n)\n\ntype NetManager struct {\n\tcurrentState ConnectivityInfo\n\n\tlocker *sync.RWMutex\n\tnotify *notify.Notify\n}\n\ntype EventType uint\n\nconst (\n\tConnectivityStateChanged EventType = 1 << iota\n\tConnectivityMeteringChanged\n\tConnectivityBluetoothChanged\n\tConnectivityNetTypeChanged\n\tConnectivityCellularTypeChanged\n\n\tConnectivityChanged = 0 |\n\t\tConnectivityStateChanged |\n\t\tConnectivityMeteringChanged |\n\t\tConnectivityBluetoothChanged |\n\t\tConnectivityNetTypeChanged |\n\t\tConnectivityCellularTypeChanged\n)\n\nfunc (t EventType) Has(other EventType) bool {\n\treturn (t & other) == other\n}\n\nfunc NewNetManager(initialState ConnectivityInfo) *NetManager {\n\tvar locker sync.RWMutex\n\treturn &NetManager{\n\t\tcurrentState: initialState,\n\t\tlocker:       &locker,\n\t\tnotify:       notify.New(&locker),\n\t}\n}\n\n// UpdateState update the current state of the Manager\nfunc (m *NetManager) UpdateState(state ConnectivityInfo) {\n\tm.locker.Lock()\n\tif m.currentState != state {\n\t\tm.currentState = state\n\t\tm.notify.Broadcast()\n\t}\n\tm.locker.Unlock()\n}\n\n// WaitForStateChange waits until the currentState changes from sourceState or ctx expires.\n// The eventType argument allow you to filter out the event you want to wait for.\n// A true value is returned in former case and false in latter.\n// The EventType is also returned to know which events has been triggered.\nfunc (m *NetManager) WaitForStateChange(ctx context.Context, sourceState *ConnectivityInfo, eventType EventType) (bool, EventType) {\n\tif ctx.Err() != nil {\n\t\treturn false, 0\n\t}\n\n\tm.locker.Lock()\n\tdefer m.locker.Unlock()\n\n\tvar currentEventType EventType\n\tok := true\n\n\tfor ok {\n\t\tcurrentEventType = 0\n\n\t\tif sourceState.State != m.currentState.State {\n\t\t\tcurrentEventType |= ConnectivityStateChanged\n\t\t}\n\t\tif sourceState.Metering != m.currentState.Metering {\n\t\t\tcurrentEventType |= ConnectivityMeteringChanged\n\t\t}\n\t\tif sourceState.Bluetooth != m.currentState.Bluetooth {\n\t\t\tcurrentEventType |= ConnectivityBluetoothChanged\n\t\t}\n\t\tif sourceState.NetType != m.currentState.NetType {\n\t\t\tcurrentEventType |= ConnectivityNetTypeChanged\n\t\t}\n\t\tif sourceState.CellularType != m.currentState.CellularType {\n\t\t\tcurrentEventType |= ConnectivityCellularTypeChanged\n\t\t}\n\n\t\tif (eventType & currentEventType) != 0 {\n\t\t\tbreak\n\t\t}\n\t\t// wait until state has been changed or context has been cancel\n\t\tok = m.notify.Wait(ctx)\n\t}\n\n\treturn ok, currentEventType\n}\n\n// GetCurrentState return the current state of the Manager\nfunc (m *NetManager) GetCurrentState() (state ConnectivityInfo) {\n\tm.locker.RLock()\n\tstate = m.currentState\n\tm.locker.RUnlock()\n\treturn\n}\n"
  },
  {
    "path": "pkg/netmanager/netmanager_noop.go",
    "content": "package netmanager\n\nfunc NewNoopNetManager() *NetManager {\n\treturn NewNetManager(ConnectivityInfo{\n\t\tState:   ConnectivityStateOn,\n\t\tNetType: ConnectivityNetWifi,\n\t})\n}\n"
  },
  {
    "path": "pkg/netmanager/netmanager_test.go",
    "content": "package netmanager\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestNewNetManager(t *testing.T) {\n\tinitial := ConnectivityInfo{\n\t\tState:     ConnectivityStateOn,\n\t\tNetType:   ConnectivityNetWifi,\n\t\tBluetooth: ConnectivityStateOn,\n\t}\n\n\tnetmanager := NewNetManager(initial)\n\n\trequire.Equal(t, initial, netmanager.GetCurrentState())\n\tinitial.State = ConnectivityStateOff\n\trequire.NotEqual(t, initial, netmanager.GetCurrentState())\n}\n\nfunc TestNetManagerSingleUpdate(t *testing.T) {\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\ta := ConnectivityInfo{\n\t\tState: ConnectivityStateOn,\n\t}\n\tstate := ConnectivityInfo{}\n\n\tnetmanager := NewNetManager(state)\n\n\tnetmanager.UpdateState(a)\n\trequire.Equal(t, a, netmanager.GetCurrentState())\n\n\tok, eventType := netmanager.WaitForStateChange(ctx, &state, ConnectivityChanged)\n\n\trequire.Equal(t, a, netmanager.GetCurrentState())\n\trequire.True(t, ok)\n\trequire.Equal(t, ConnectivityStateChanged, eventType)\n}\n\nfunc TestNetManagerDoubleUpdate(t *testing.T) {\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\ta := ConnectivityInfo{\n\t\tState: ConnectivityStateOn,\n\t}\n\tb := ConnectivityInfo{\n\t\tState: ConnectivityStateOff,\n\t}\n\tstate := ConnectivityInfo{}\n\n\tnetmanager := NewNetManager(state)\n\n\tnetmanager.UpdateState(a)\n\trequire.Equal(t, a, netmanager.GetCurrentState())\n\tnetmanager.UpdateState(b)\n\trequire.Equal(t, b, netmanager.GetCurrentState())\n\n\tok, eventType := netmanager.WaitForStateChange(ctx, &state, ConnectivityChanged)\n\n\trequire.Equal(t, b, netmanager.GetCurrentState())\n\trequire.True(t, ok)\n\trequire.Equal(t, ConnectivityStateChanged, eventType)\n}\n\nfunc TestNetManagerFilterUpdate(t *testing.T) {\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\ta := ConnectivityInfo{\n\t\tState: ConnectivityStateOff,\n\t}\n\tb := ConnectivityInfo{\n\t\tState:        ConnectivityStateOn,\n\t\tNetType:      ConnectivityNetCellular,\n\t\tCellularType: ConnectivityCellular3G,\n\t}\n\tstate := ConnectivityInfo{}\n\n\tnetmanager := NewNetManager(state)\n\n\tnetmanager.UpdateState(a)\n\trequire.Equal(t, a, netmanager.GetCurrentState())\n\tnetmanager.UpdateState(b)\n\trequire.Equal(t, b, netmanager.GetCurrentState())\n\n\tok, eventType := netmanager.WaitForStateChange(ctx, &state, ConnectivityCellularTypeChanged)\n\n\trequire.Equal(t, b, netmanager.GetCurrentState())\n\trequire.True(t, ok)\n\trequire.Equal(t, ConnectivityStateChanged|ConnectivityNetTypeChanged|ConnectivityCellularTypeChanged, eventType)\n}\n"
  },
  {
    "path": "pkg/outofstoremessage/outofstoremessage_test.go",
    "content": "package outofstoremessage\n\nimport (\n\t\"context\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/ipfs/go-cid\"\n\t\"github.com/stretchr/testify/require\"\n\t\"google.golang.org/protobuf/proto\"\n\n\t\"berty.tech/weshnet/v2\"\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n\t\"berty.tech/weshnet/v2/pkg/secretstore\"\n\t\"berty.tech/weshnet/v2/pkg/testutil\"\n)\n\nfunc Test_sealPushMessage_OutOfStoreReceive(t *testing.T) {\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\ttp, cancel := weshnet.NewTestingProtocol(ctx, t, &weshnet.TestingOpts{}, nil)\n\tdefer cancel()\n\n\tg, _, err := weshnet.NewGroupMultiMember()\n\trequire.NoError(t, err)\n\n\ts := tp.Service\n\n\tgPK, err := g.GetPubKey()\n\trequire.NoError(t, err)\n\n\t_, err = s.MultiMemberGroupJoin(ctx, &protocoltypes.MultiMemberGroupJoin_Request{Group: g})\n\trequire.NoError(t, err)\n\n\tgPKRaw, err := gPK.Raw()\n\trequire.NoError(t, err)\n\n\t_, err = s.ActivateGroup(ctx, &protocoltypes.ActivateGroup_Request{GroupPk: gPKRaw})\n\trequire.NoError(t, err)\n\n\tgc, err := s.(weshnet.ServiceMethods).GetContextGroupForID(g.PublicKey)\n\trequire.NoError(t, err)\n\n\totherSecretStore, cancel := createVirtualOtherPeerSecrets(t, ctx, gc)\n\tdefer cancel()\n\n\ttestPayload := []byte(\"test payload\")\n\n\tenvBytes, err := otherSecretStore.SealEnvelope(ctx, g, testPayload)\n\trequire.NoError(t, err)\n\n\tenv, headers, err := otherSecretStore.OpenEnvelopeHeaders(envBytes, g)\n\trequire.NoError(t, err)\n\n\toosMsgEnv, err := otherSecretStore.SealOutOfStoreMessageEnvelope(cid.Undef, env, headers, g)\n\trequire.NoError(t, err)\n\toosMsgEnvBytes, err := proto.Marshal(oosMsgEnv)\n\trequire.NoError(t, err)\n\n\toutOfStoreMessage, group, clearPayload, alreadyDecrypted, err := gc.SecretStore().OpenOutOfStoreMessage(ctx, oosMsgEnvBytes)\n\trequire.NoError(t, err)\n\n\trequire.Equal(t, g.PublicKey, group.PublicKey)\n\trequire.Equal(t, g.Secret, group.Secret)\n\trequire.Equal(t, g.SecretSig, group.SecretSig)\n\trequire.Equal(t, g.GroupType, group.GroupType)\n\trequire.Equal(t, g.SignPub, group.SignPub)\n\trequire.Equal(t, g.LinkKey, group.LinkKey)\n\trequire.Equal(t, g.LinkKeySig, group.LinkKeySig)\n\trequire.Equal(t, []byte(\"test payload\"), clearPayload)\n\trequire.False(t, alreadyDecrypted)\n\n\trequire.Equal(t, headers.Counter, outOfStoreMessage.Counter)\n\trequire.Equal(t, headers.DevicePk, outOfStoreMessage.DevicePk)\n\trequire.Equal(t, headers.Sig, outOfStoreMessage.Sig)\n\trequire.Equal(t, env.Message, outOfStoreMessage.EncryptedPayload)\n}\n\nfunc Test_OutOfStoreMessageFlow(t *testing.T) {\n\tmessage := []byte(\"test message\")\n\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\tlogger, cleanup := testutil.Logger(t)\n\tdefer cleanup()\n\n\ttp, cancel := weshnet.NewTestingProtocol(ctx, t, &weshnet.TestingOpts{Logger: logger}, nil)\n\tdefer cancel()\n\n\tg, _, err := weshnet.NewGroupMultiMember()\n\trequire.NoError(t, err)\n\n\ts := tp.Service\n\n\tgPK, err := g.GetPubKey()\n\trequire.NoError(t, err)\n\n\t_, err = s.MultiMemberGroupJoin(ctx, &protocoltypes.MultiMemberGroupJoin_Request{Group: g})\n\trequire.NoError(t, err)\n\n\tgPKRaw, err := gPK.Raw()\n\trequire.NoError(t, err)\n\n\t_, err = s.ActivateGroup(ctx, &protocoltypes.ActivateGroup_Request{GroupPk: gPKRaw})\n\trequire.NoError(t, err)\n\n\t// send a message\n\tsendReply, err := s.AppMessageSend(ctx, &protocoltypes.AppMessageSend_Request{\n\t\tGroupPk: gPKRaw,\n\t\tPayload: message,\n\t})\n\trequire.NoError(t, err)\n\n\ttime.Sleep(100 * time.Millisecond)\n\n\t// craft an out of store message\n\tcraftReply, err := s.OutOfStoreSeal(ctx, &protocoltypes.OutOfStoreSeal_Request{\n\t\tCid:            sendReply.Cid,\n\t\tGroupPublicKey: gPKRaw,\n\t})\n\trequire.NoError(t, err)\n\n\t// verify the out of store message\n\topenReply, err := s.OutOfStoreReceive(ctx, &protocoltypes.OutOfStoreReceive_Request{\n\t\tPayload: craftReply.Encrypted,\n\t})\n\trequire.NoError(t, err)\n\n\tencryptedMessage := &protocoltypes.EncryptedMessage{}\n\terr = proto.Unmarshal(openReply.Cleartext, encryptedMessage)\n\trequire.NoError(t, err)\n\n\trequire.Equal(t, message, encryptedMessage.Plaintext)\n}\n\nfunc createVirtualOtherPeerSecrets(t testing.TB, ctx context.Context, gc *weshnet.GroupContext) (secretstore.SecretStore, func()) {\n\tsecretStore, err := secretstore.NewInMemSecretStore(nil)\n\trequire.NoError(t, err)\n\n\tcleanup := func() {\n\t\t_ = secretStore.Close()\n\t}\n\n\t// Manually adding another member to the group\n\totherMD, err := secretStore.GetOwnMemberDeviceForGroup(gc.Group())\n\trequire.NoError(t, err)\n\t_, err = weshnet.MetadataStoreAddDeviceToGroup(ctx, gc.MetadataStore(), gc.Group(), otherMD)\n\trequire.NoError(t, err)\n\n\tmemberDevice, err := gc.SecretStore().GetOwnMemberDeviceForGroup(gc.Group())\n\trequire.NoError(t, err)\n\n\tds, err := secretStore.GetShareableChainKey(ctx, gc.Group(), memberDevice.Member())\n\trequire.NoError(t, err)\n\n\t_, err = weshnet.MetadataStoreSendSecret(ctx, gc.MetadataStore(), gc.Group(), otherMD, memberDevice.Member(), ds)\n\trequire.NoError(t, err)\n\n\ttime.Sleep(time.Millisecond * 200)\n\n\treturn secretStore, cleanup\n}\n"
  },
  {
    "path": "pkg/outofstoremessage/service_outofstoremessage.go",
    "content": "package outofstoremessage\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"time\"\n\n\tds \"github.com/ipfs/go-datastore\"\n\tds_sync \"github.com/ipfs/go-datastore/sync\"\n\tcoreiface \"github.com/ipfs/kubo/core/coreiface\"\n\t\"go.uber.org/zap\"\n\t\"google.golang.org/grpc\"\n\n\t\"berty.tech/weshnet/v2\"\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n\t\"berty.tech/weshnet/v2/pkg/grpcutil\"\n\t\"berty.tech/weshnet/v2/pkg/outofstoremessagetypes\"\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n\t\"berty.tech/weshnet/v2/pkg/secretstore\"\n)\n\ntype OOSMService interface {\n\toutofstoremessagetypes.OutOfStoreMessageServiceServer\n}\n\nvar _ OOSMService = (*oosmService)(nil)\n\ntype oosmService struct {\n\tlogger        *zap.Logger\n\trootDatastore ds.Datastore\n\tsecretStore   secretstore.SecretStore\n\n\toutofstoremessagetypes.UnimplementedOutOfStoreMessageServiceServer\n}\n\ntype OOSMServiceClient interface {\n\toutofstoremessagetypes.OutOfStoreMessageServiceClient\n\n\tio.Closer\n}\n\ntype oosmServiceClient struct {\n\tOOSMServiceClient\n\n\tservice OOSMService\n\tserver  *grpc.Server\n}\n\ntype OOSMOption func(*oosmService) error\n\n// NewOutOfStoreMessageServiceClient creates a new Wesh protocol service and returns a gRPC\n// ServiceClient which uses a direct in-memory connection. When finished, you must call Close().\n// This opens or creates a Wesh account where the datastore location is specified by the path argument.\n// The service will not start any network stuff, it will only use the filesystem to store or get data.\nfunc NewOutOfStoreMessageServiceClient(opts ...OOSMOption) (OOSMServiceClient, error) {\n\tsvc, err := NewOutOfStoreMessageService(opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\ts := grpc.NewServer()\n\n\tctx, cancel := context.WithTimeout(context.Background(), time.Second*10)\n\tdefer cancel()\n\n\tc, err := newClientFromService(ctx, s, svc)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to create client from server: %w\", err)\n\t}\n\n\treturn &oosmServiceClient{\n\t\tOOSMServiceClient: c,\n\t\tserver:            s,\n\t\tservice:           svc,\n\t}, nil\n}\n\ntype oosmClient struct {\n\toutofstoremessagetypes.OutOfStoreMessageServiceClient\n\n\tl  *grpcutil.BufListener\n\tcc *grpc.ClientConn\n}\n\nfunc (c *oosmClient) Close() error {\n\terr := c.cc.Close()\n\t_ = c.l.Close()\n\treturn err\n}\n\nfunc newClientFromService(ctx context.Context, s *grpc.Server, svc OOSMService, opts ...grpc.DialOption) (OOSMServiceClient, error) {\n\tbl := grpcutil.NewBufListener(weshnet.ClientBufferSize)\n\tcc, err := bl.NewClientConn(ctx, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\toutofstoremessagetypes.RegisterOutOfStoreMessageServiceServer(s, svc)\n\tgo func() {\n\t\t// we dont need to log the error\n\t\t_ = s.Serve(bl)\n\t}()\n\n\treturn &oosmClient{\n\t\tOutOfStoreMessageServiceClient: outofstoremessagetypes.NewOutOfStoreMessageServiceClient(cc),\n\t\tcc:                             cc,\n\t\tl:                              bl,\n\t}, nil\n}\n\nfunc NewOutOfStoreMessageService(opts ...OOSMOption) (OOSMService, error) {\n\tsvc := &oosmService{}\n\n\twithDefaultOpts := make([]OOSMOption, len(opts))\n\tcopy(withDefaultOpts, opts)\n\twithDefaultOpts = append(withDefaultOpts, WithFallbackDefaults)\n\tfor _, opt := range withDefaultOpts {\n\t\tif err := opt(svc); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\treturn svc, nil\n}\n\nfunc (s *oosmService) Close() error {\n\treturn nil\n}\n\nfunc (s *oosmService) Status() (weshnet.Status, error) {\n\treturn weshnet.Status{}, nil\n}\n\nfunc (s *oosmService) IpfsCoreAPI() coreiface.CoreAPI {\n\treturn nil\n}\n\nfunc (s *oosmService) OutOfStoreReceive(ctx context.Context, request *protocoltypes.OutOfStoreReceive_Request) (*protocoltypes.OutOfStoreReceive_Reply, error) {\n\toutOfStoreMessage, group, clearPayload, alreadyDecrypted, err := s.secretStore.OpenOutOfStoreMessage(ctx, request.Payload)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrCryptoDecrypt.Wrap(err)\n\t}\n\n\treturn &protocoltypes.OutOfStoreReceive_Reply{\n\t\tMessage:         outOfStoreMessage,\n\t\tCleartext:       clearPayload,\n\t\tGroupPublicKey:  group.PublicKey,\n\t\tAlreadyReceived: alreadyDecrypted,\n\t}, nil\n}\n\n// FallBackOption is a structure that permit to fallback to a default option if the option is not set.\ntype FallBackOption struct {\n\tfallback func(s *oosmService) bool\n\topt      OOSMOption\n}\n\n// WithLogger set the given logger.\nvar WithLogger = func(l *zap.Logger) OOSMOption {\n\treturn func(s *oosmService) error {\n\t\ts.logger = l\n\t\treturn nil\n\t}\n}\n\n// WithDefaultLogger init a noop logger.\nvar WithDefaultLogger OOSMOption = func(s *oosmService) error {\n\ts.logger = zap.NewNop()\n\treturn nil\n}\n\nvar fallbackLogger = FallBackOption{\n\tfallback: func(s *oosmService) bool { return s.logger == nil },\n\topt:      WithDefaultLogger,\n}\n\n// WithFallbackLogger set the logger if no logger is set.\nvar WithFallbackLogger OOSMOption = func(s *oosmService) error {\n\tif fallbackLogger.fallback(s) {\n\t\treturn fallbackLogger.opt(s)\n\t}\n\treturn nil\n}\n\n// WithRootDatastore set the root datastore.\nvar WithRootDatastore = func(ds ds.Datastore) OOSMOption {\n\treturn func(s *oosmService) error {\n\t\ts.rootDatastore = ds\n\t\treturn nil\n\t}\n}\n\n// WithDefaultRootDatastore init a in-memory datastore.\nvar WithDefaultRootDatastore OOSMOption = func(s *oosmService) error {\n\ts.rootDatastore = ds_sync.MutexWrap(ds.NewMapDatastore())\n\treturn nil\n}\n\nvar fallbackRootDatastore = FallBackOption{\n\tfallback: func(s *oosmService) bool { return s.rootDatastore == nil },\n\topt:      WithDefaultRootDatastore,\n}\n\n// WithFallbackRootDatastore set the root datastore if no root datastore is set.\nvar WithFallbackRootDatastore OOSMOption = func(s *oosmService) error {\n\tif fallbackRootDatastore.fallback(s) {\n\t\treturn fallbackRootDatastore.opt(s)\n\t}\n\treturn nil\n}\n\n// WithSecretStore set the secret store.\nvar WithSecretStore = func(ss secretstore.SecretStore) OOSMOption {\n\treturn func(s *oosmService) error {\n\t\ts.secretStore = ss\n\t\treturn nil\n\t}\n}\n\n// WithDefaultSecretStore init a new secret store.\n// Call WithRootDatastore before this option if you want to use your datastore.\n// Call WithLogger before this option if you want to use your logger.\nvar WithDefaultSecretStore OOSMOption = func(s *oosmService) error {\n\t// dependency\n\tif err := WithFallbackRootDatastore(s); err != nil {\n\t\treturn err\n\t}\n\tif err := WithFallbackLogger(s); err != nil {\n\t\treturn err\n\t}\n\n\tvar err error\n\ts.secretStore, err = secretstore.NewSecretStore(s.rootDatastore, &secretstore.NewSecretStoreOptions{\n\t\tLogger: s.logger,\n\t})\n\treturn err\n}\n\nvar fallbackSecretStore = FallBackOption{\n\tfallback: func(s *oosmService) bool { return s.secretStore == nil },\n\topt:      WithDefaultSecretStore,\n}\n\n// WithFallbackSecretStore set the secret store if no secret store is set.\n// Call WithRootDatastore before this option if you want to use your datastore if a new secret store is created.\n// Call WithLogger before this option if you want to use your logger if a new secret store is created.\nvar WithFallbackSecretStore OOSMOption = func(s *oosmService) error {\n\tif fallbackSecretStore.fallback(s) {\n\t\treturn fallbackSecretStore.opt(s)\n\t}\n\treturn nil\n}\n\nvar defaults = []FallBackOption{\n\tfallbackLogger,\n\tfallbackRootDatastore,\n\tfallbackSecretStore,\n}\n\n// WithFallbackDefaults set the default options if no option is set.\nvar WithFallbackDefaults OOSMOption = func(s *oosmService) error {\n\tfor _, def := range defaults {\n\t\tif !def.fallback(s) {\n\t\t\tcontinue\n\t\t}\n\t\tif err := def.opt(s); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/outofstoremessagetypes/outofstoremessage.pb.go",
    "content": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.34.2\n// \tprotoc        (unknown)\n// source: outofstoremessagetypes/outofstoremessage.proto\n\npackage outofstoremessagetypes\n\nimport (\n\tprotocoltypes \"berty.tech/weshnet/v2/pkg/protocoltypes\"\n\tprotoreflect \"google.golang.org/protobuf/reflect/protoreflect\"\n\tprotoimpl \"google.golang.org/protobuf/runtime/protoimpl\"\n\treflect \"reflect\"\n)\n\nconst (\n\t// Verify that this generated code is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)\n\t// Verify that runtime/protoimpl is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)\n)\n\nvar File_outofstoremessagetypes_outofstoremessage_proto protoreflect.FileDescriptor\n\nvar file_outofstoremessagetypes_outofstoremessage_proto_rawDesc = []byte{\n\t0x0a, 0x2e, 0x6f, 0x75, 0x74, 0x6f, 0x66, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x6d, 0x65, 0x73, 0x73,\n\t0x61, 0x67, 0x65, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x6f, 0x75, 0x74, 0x6f, 0x66, 0x73, 0x74,\n\t0x6f, 0x72, 0x65, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,\n\t0x12, 0x1c, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x6f, 0x75, 0x74, 0x6f, 0x66, 0x73,\n\t0x74, 0x6f, 0x72, 0x65, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x13,\n\t0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x70, 0x72,\n\t0x6f, 0x74, 0x6f, 0x32, 0x8d, 0x01, 0x0a, 0x18, 0x4f, 0x75, 0x74, 0x4f, 0x66, 0x53, 0x74, 0x6f,\n\t0x72, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,\n\t0x12, 0x71, 0x0a, 0x11, 0x4f, 0x75, 0x74, 0x4f, 0x66, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65,\n\t0x63, 0x65, 0x69, 0x76, 0x65, 0x12, 0x2e, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e,\n\t0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x4f,\n\t0x66, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x2e, 0x52, 0x65,\n\t0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e,\n\t0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x4f,\n\t0x66, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x2e, 0x52, 0x65,\n\t0x70, 0x6c, 0x79, 0x42, 0x32, 0x5a, 0x30, 0x62, 0x65, 0x72, 0x74, 0x79, 0x2e, 0x74, 0x65, 0x63,\n\t0x68, 0x2f, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2f, 0x76, 0x32, 0x2f, 0x70, 0x6b, 0x67,\n\t0x2f, 0x6f, 0x75, 0x74, 0x6f, 0x66, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x6d, 0x65, 0x73, 0x73, 0x61,\n\t0x67, 0x65, 0x74, 0x79, 0x70, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,\n}\n\nvar file_outofstoremessagetypes_outofstoremessage_proto_goTypes = []any{\n\t(*protocoltypes.OutOfStoreReceive_Request)(nil), // 0: weshnet.protocol.v1.OutOfStoreReceive.Request\n\t(*protocoltypes.OutOfStoreReceive_Reply)(nil),   // 1: weshnet.protocol.v1.OutOfStoreReceive.Reply\n}\nvar file_outofstoremessagetypes_outofstoremessage_proto_depIdxs = []int32{\n\t0, // 0: weshnet.outofstoremessage.v1.OutOfStoreMessageService.OutOfStoreReceive:input_type -> weshnet.protocol.v1.OutOfStoreReceive.Request\n\t1, // 1: weshnet.outofstoremessage.v1.OutOfStoreMessageService.OutOfStoreReceive:output_type -> weshnet.protocol.v1.OutOfStoreReceive.Reply\n\t1, // [1:2] is the sub-list for method output_type\n\t0, // [0:1] is the sub-list for method input_type\n\t0, // [0:0] is the sub-list for extension type_name\n\t0, // [0:0] is the sub-list for extension extendee\n\t0, // [0:0] is the sub-list for field type_name\n}\n\nfunc init() { file_outofstoremessagetypes_outofstoremessage_proto_init() }\nfunc file_outofstoremessagetypes_outofstoremessage_proto_init() {\n\tif File_outofstoremessagetypes_outofstoremessage_proto != nil {\n\t\treturn\n\t}\n\ttype x struct{}\n\tout := protoimpl.TypeBuilder{\n\t\tFile: protoimpl.DescBuilder{\n\t\t\tGoPackagePath: reflect.TypeOf(x{}).PkgPath(),\n\t\t\tRawDescriptor: file_outofstoremessagetypes_outofstoremessage_proto_rawDesc,\n\t\t\tNumEnums:      0,\n\t\t\tNumMessages:   0,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   1,\n\t\t},\n\t\tGoTypes:           file_outofstoremessagetypes_outofstoremessage_proto_goTypes,\n\t\tDependencyIndexes: file_outofstoremessagetypes_outofstoremessage_proto_depIdxs,\n\t}.Build()\n\tFile_outofstoremessagetypes_outofstoremessage_proto = out.File\n\tfile_outofstoremessagetypes_outofstoremessage_proto_rawDesc = nil\n\tfile_outofstoremessagetypes_outofstoremessage_proto_goTypes = nil\n\tfile_outofstoremessagetypes_outofstoremessage_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "pkg/outofstoremessagetypes/outofstoremessage.pb.gw.go",
    "content": "// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.\n// source: outofstoremessagetypes/outofstoremessage.proto\n\n/*\nPackage outofstoremessagetypes is a reverse proxy.\n\nIt translates gRPC into RESTful JSON APIs.\n*/\npackage outofstoremessagetypes\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"net/http\"\n\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n\t\"github.com/golang/protobuf/descriptor\"\n\t\"github.com/golang/protobuf/proto\"\n\t\"github.com/grpc-ecosystem/grpc-gateway/runtime\"\n\t\"github.com/grpc-ecosystem/grpc-gateway/utilities\"\n\t\"google.golang.org/grpc\"\n\t\"google.golang.org/grpc/codes\"\n\t\"google.golang.org/grpc/grpclog\"\n\t\"google.golang.org/grpc/metadata\"\n\t\"google.golang.org/grpc/status\"\n)\n\n// Suppress \"imported and not used\" errors\nvar _ codes.Code\nvar _ io.Reader\nvar _ status.Status\nvar _ = runtime.String\nvar _ = utilities.NewDoubleArray\nvar _ = descriptor.ForMessage\nvar _ = metadata.Join\n\nfunc request_OutOfStoreMessageService_OutOfStoreReceive_0(ctx context.Context, marshaler runtime.Marshaler, client OutOfStoreMessageServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq protocoltypes.OutOfStoreReceive_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := client.OutOfStoreReceive(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))\n\treturn msg, metadata, err\n\n}\n\nfunc local_request_OutOfStoreMessageService_OutOfStoreReceive_0(ctx context.Context, marshaler runtime.Marshaler, server OutOfStoreMessageServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq protocoltypes.OutOfStoreReceive_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := server.OutOfStoreReceive(ctx, &protoReq)\n\treturn msg, metadata, err\n\n}\n\n// RegisterOutOfStoreMessageServiceHandlerServer registers the http handlers for service OutOfStoreMessageService to \"mux\".\n// UnaryRPC     :call OutOfStoreMessageServiceServer directly.\n// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.\n// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterOutOfStoreMessageServiceHandlerFromEndpoint instead.\nfunc RegisterOutOfStoreMessageServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server OutOfStoreMessageServiceServer) error {\n\n\tmux.Handle(\"POST\", pattern_OutOfStoreMessageService_OutOfStoreReceive_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tvar stream runtime.ServerTransportStream\n\t\tctx = grpc.NewContextWithServerTransportStream(ctx, &stream)\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := local_request_OutOfStoreMessageService_OutOfStoreReceive_0(rctx, inboundMarshaler, server, req, pathParams)\n\t\tmd.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_OutOfStoreMessageService_OutOfStoreReceive_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\treturn nil\n}\n\n// RegisterOutOfStoreMessageServiceHandlerFromEndpoint is same as RegisterOutOfStoreMessageServiceHandler but\n// automatically dials to \"endpoint\" and closes the connection when \"ctx\" gets done.\nfunc RegisterOutOfStoreMessageServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {\n\tconn, err := grpc.Dial(endpoint, opts...)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer func() {\n\t\tif err != nil {\n\t\t\tif cerr := conn.Close(); cerr != nil {\n\t\t\t\tgrpclog.Infof(\"Failed to close conn to %s: %v\", endpoint, cerr)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\tgo func() {\n\t\t\t<-ctx.Done()\n\t\t\tif cerr := conn.Close(); cerr != nil {\n\t\t\t\tgrpclog.Infof(\"Failed to close conn to %s: %v\", endpoint, cerr)\n\t\t\t}\n\t\t}()\n\t}()\n\n\treturn RegisterOutOfStoreMessageServiceHandler(ctx, mux, conn)\n}\n\n// RegisterOutOfStoreMessageServiceHandler registers the http handlers for service OutOfStoreMessageService to \"mux\".\n// The handlers forward requests to the grpc endpoint over \"conn\".\nfunc RegisterOutOfStoreMessageServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {\n\treturn RegisterOutOfStoreMessageServiceHandlerClient(ctx, mux, NewOutOfStoreMessageServiceClient(conn))\n}\n\n// RegisterOutOfStoreMessageServiceHandlerClient registers the http handlers for service OutOfStoreMessageService\n// to \"mux\". The handlers forward requests to the grpc endpoint over the given implementation of \"OutOfStoreMessageServiceClient\".\n// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in \"OutOfStoreMessageServiceClient\"\n// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in\n// \"OutOfStoreMessageServiceClient\" to call the correct interceptors.\nfunc RegisterOutOfStoreMessageServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client OutOfStoreMessageServiceClient) error {\n\n\tmux.Handle(\"POST\", pattern_OutOfStoreMessageService_OutOfStoreReceive_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := request_OutOfStoreMessageService_OutOfStoreReceive_0(rctx, inboundMarshaler, client, req, pathParams)\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_OutOfStoreMessageService_OutOfStoreReceive_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\treturn nil\n}\n\nvar (\n\tpattern_OutOfStoreMessageService_OutOfStoreReceive_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{\"weshnet.outofstoremessage.v1.OutOfStoreMessageService\", \"OutOfStoreReceive\"}, \"\", runtime.AssumeColonVerbOpt(true)))\n)\n\nvar (\n\tforward_OutOfStoreMessageService_OutOfStoreReceive_0 = runtime.ForwardResponseMessage\n)\n"
  },
  {
    "path": "pkg/outofstoremessagetypes/outofstoremessage_grpc.pb.go",
    "content": "// Code generated by protoc-gen-go-grpc. DO NOT EDIT.\n// versions:\n// - protoc-gen-go-grpc v1.5.1\n// - protoc             (unknown)\n// source: outofstoremessagetypes/outofstoremessage.proto\n\npackage outofstoremessagetypes\n\nimport (\n\tprotocoltypes \"berty.tech/weshnet/v2/pkg/protocoltypes\"\n\tcontext \"context\"\n\tgrpc \"google.golang.org/grpc\"\n\tcodes \"google.golang.org/grpc/codes\"\n\tstatus \"google.golang.org/grpc/status\"\n)\n\n// This is a compile-time assertion to ensure that this generated file\n// is compatible with the grpc package it is being compiled against.\n// Requires gRPC-Go v1.64.0 or later.\nconst _ = grpc.SupportPackageIsVersion9\n\nconst (\n\tOutOfStoreMessageService_OutOfStoreReceive_FullMethodName = \"/weshnet.outofstoremessage.v1.OutOfStoreMessageService/OutOfStoreReceive\"\n)\n\n// OutOfStoreMessageServiceClient is the client API for OutOfStoreMessageService service.\n//\n// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.\n//\n// OutOfStoreMessageService is the service used to open out-of-store messages (e.g. push notifications)\n// It is used to open messages with a lightweight protocol service for mobile backgroup processes.\ntype OutOfStoreMessageServiceClient interface {\n\t// OutOfStoreReceive parses a payload received outside a synchronized store\n\tOutOfStoreReceive(ctx context.Context, in *protocoltypes.OutOfStoreReceive_Request, opts ...grpc.CallOption) (*protocoltypes.OutOfStoreReceive_Reply, error)\n}\n\ntype outOfStoreMessageServiceClient struct {\n\tcc grpc.ClientConnInterface\n}\n\nfunc NewOutOfStoreMessageServiceClient(cc grpc.ClientConnInterface) OutOfStoreMessageServiceClient {\n\treturn &outOfStoreMessageServiceClient{cc}\n}\n\nfunc (c *outOfStoreMessageServiceClient) OutOfStoreReceive(ctx context.Context, in *protocoltypes.OutOfStoreReceive_Request, opts ...grpc.CallOption) (*protocoltypes.OutOfStoreReceive_Reply, error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tout := new(protocoltypes.OutOfStoreReceive_Reply)\n\terr := c.cc.Invoke(ctx, OutOfStoreMessageService_OutOfStoreReceive_FullMethodName, in, out, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\n// OutOfStoreMessageServiceServer is the server API for OutOfStoreMessageService service.\n// All implementations must embed UnimplementedOutOfStoreMessageServiceServer\n// for forward compatibility.\n//\n// OutOfStoreMessageService is the service used to open out-of-store messages (e.g. push notifications)\n// It is used to open messages with a lightweight protocol service for mobile backgroup processes.\ntype OutOfStoreMessageServiceServer interface {\n\t// OutOfStoreReceive parses a payload received outside a synchronized store\n\tOutOfStoreReceive(context.Context, *protocoltypes.OutOfStoreReceive_Request) (*protocoltypes.OutOfStoreReceive_Reply, error)\n\tmustEmbedUnimplementedOutOfStoreMessageServiceServer()\n}\n\n// UnimplementedOutOfStoreMessageServiceServer must be embedded to have\n// forward compatible implementations.\n//\n// NOTE: this should be embedded by value instead of pointer to avoid a nil\n// pointer dereference when methods are called.\ntype UnimplementedOutOfStoreMessageServiceServer struct{}\n\nfunc (UnimplementedOutOfStoreMessageServiceServer) OutOfStoreReceive(context.Context, *protocoltypes.OutOfStoreReceive_Request) (*protocoltypes.OutOfStoreReceive_Reply, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method OutOfStoreReceive not implemented\")\n}\nfunc (UnimplementedOutOfStoreMessageServiceServer) mustEmbedUnimplementedOutOfStoreMessageServiceServer() {\n}\nfunc (UnimplementedOutOfStoreMessageServiceServer) testEmbeddedByValue() {}\n\n// UnsafeOutOfStoreMessageServiceServer may be embedded to opt out of forward compatibility for this service.\n// Use of this interface is not recommended, as added methods to OutOfStoreMessageServiceServer will\n// result in compilation errors.\ntype UnsafeOutOfStoreMessageServiceServer interface {\n\tmustEmbedUnimplementedOutOfStoreMessageServiceServer()\n}\n\nfunc RegisterOutOfStoreMessageServiceServer(s grpc.ServiceRegistrar, srv OutOfStoreMessageServiceServer) {\n\t// If the following call pancis, it indicates UnimplementedOutOfStoreMessageServiceServer was\n\t// embedded by pointer and is nil.  This will cause panics if an\n\t// unimplemented method is ever invoked, so we test this at initialization\n\t// time to prevent it from happening at runtime later due to I/O.\n\tif t, ok := srv.(interface{ testEmbeddedByValue() }); ok {\n\t\tt.testEmbeddedByValue()\n\t}\n\ts.RegisterService(&OutOfStoreMessageService_ServiceDesc, srv)\n}\n\nfunc _OutOfStoreMessageService_OutOfStoreReceive_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(protocoltypes.OutOfStoreReceive_Request)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(OutOfStoreMessageServiceServer).OutOfStoreReceive(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: OutOfStoreMessageService_OutOfStoreReceive_FullMethodName,\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(OutOfStoreMessageServiceServer).OutOfStoreReceive(ctx, req.(*protocoltypes.OutOfStoreReceive_Request))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\n// OutOfStoreMessageService_ServiceDesc is the grpc.ServiceDesc for OutOfStoreMessageService service.\n// It's only intended for direct use with grpc.RegisterService,\n// and not to be introspected or modified (even as a copy)\nvar OutOfStoreMessageService_ServiceDesc = grpc.ServiceDesc{\n\tServiceName: \"weshnet.outofstoremessage.v1.OutOfStoreMessageService\",\n\tHandlerType: (*OutOfStoreMessageServiceServer)(nil),\n\tMethods: []grpc.MethodDesc{\n\t\t{\n\t\t\tMethodName: \"OutOfStoreReceive\",\n\t\t\tHandler:    _OutOfStoreMessageService_OutOfStoreReceive_Handler,\n\t\t},\n\t},\n\tStreams:  []grpc.StreamDesc{},\n\tMetadata: \"outofstoremessagetypes/outofstoremessage.proto\",\n}\n"
  },
  {
    "path": "pkg/protocoltypes/contact.go",
    "content": "package protocoltypes\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/libp2p/go-libp2p/core/crypto\"\n\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n)\n\nconst RendezvousSeedLength = 32\n\ntype ShareableContactOptions uint64\n\nconst (\n\tshareableContactOptionsUndefined = iota\n\tShareableContactOptionsAllowMissingRDVSeed\n\tShareableContactOptionsAllowMissingPK\n)\n\nvar _ = shareableContactOptionsUndefined\n\nfunc (m *ShareableContact) CheckFormat(options ...ShareableContactOptions) error {\n\tvar (\n\t\toptionMissingPKAllowed      = false\n\t\toptionMissingRDVSeedAllowed = false\n\t)\n\n\tfor _, o := range options {\n\t\tif o == ShareableContactOptionsAllowMissingPK {\n\t\t\toptionMissingPKAllowed = true\n\t\t}\n\n\t\tif o == ShareableContactOptionsAllowMissingRDVSeed {\n\t\t\toptionMissingRDVSeedAllowed = true\n\t\t}\n\t}\n\n\tif l := len(m.PublicRendezvousSeed); l != RendezvousSeedLength {\n\t\tif !(l == 0 && optionMissingRDVSeedAllowed) {\n\t\t\treturn errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"rendezvous seed length should not be %d\", l))\n\t\t}\n\t}\n\n\tif l := len(m.Pk); l == 0 && !optionMissingPKAllowed {\n\t\treturn errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"contact public key is missing\"))\n\t}\n\n\tif l := len(m.Pk); l != 0 {\n\t\t_, err := crypto.UnmarshalEd25519PublicKey(m.Pk)\n\t\tif err != nil {\n\t\t\treturn errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (m *ShareableContact) IsSamePK(otherPK crypto.PubKey) bool {\n\tpk, err := m.GetPubKey()\n\tif err != nil {\n\t\treturn false\n\t}\n\n\treturn otherPK.Equals(pk)\n}\n\nfunc (m *ShareableContact) GetPubKey() (crypto.PubKey, error) {\n\tpk, err := crypto.UnmarshalEd25519PublicKey(m.Pk)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\treturn pk, nil\n}\n"
  },
  {
    "path": "pkg/protocoltypes/doc.go",
    "content": "// Package protocoltypes contains types and helpers for the Berty Protocol.\n\n// This package is generated with Protobuf.\n// See https://github.com/berty/berty/tree/master/api for more information.\npackage protocoltypes\n"
  },
  {
    "path": "pkg/protocoltypes/events_account.go",
    "content": "package protocoltypes\n\nfunc (m *AccountGroupJoined) SetDevicePK(pk []byte) {\n\tm.DevicePk = pk\n}\n\nfunc (m *AccountGroupLeft) SetDevicePK(pk []byte) {\n\tm.DevicePk = pk\n}\n\nfunc (m *AccountContactRequestDisabled) SetDevicePK(pk []byte) {\n\tm.DevicePk = pk\n}\n\nfunc (m *AccountContactRequestEnabled) SetDevicePK(pk []byte) {\n\tm.DevicePk = pk\n}\n\nfunc (m *AccountContactRequestReferenceReset) SetDevicePK(pk []byte) {\n\tm.DevicePk = pk\n}\n\nfunc (m *AccountContactRequestOutgoingEnqueued) SetDevicePK(pk []byte) {\n\tm.DevicePk = pk\n}\n\nfunc (m *AccountContactRequestOutgoingSent) SetDevicePK(pk []byte) {\n\tm.DevicePk = pk\n}\n\nfunc (m *AccountContactRequestIncomingReceived) SetDevicePK(pk []byte) {\n\tm.DevicePk = pk\n}\n\nfunc (m *AccountContactRequestIncomingDiscarded) SetDevicePK(pk []byte) {\n\tm.DevicePk = pk\n}\n\nfunc (m *AccountContactRequestIncomingAccepted) SetDevicePK(pk []byte) {\n\tm.DevicePk = pk\n}\n\nfunc (m *AccountContactBlocked) SetDevicePK(pk []byte) {\n\tm.DevicePk = pk\n}\n\nfunc (m *AccountContactUnblocked) SetDevicePK(pk []byte) {\n\tm.DevicePk = pk\n}\n\nfunc (m *AccountContactRequestOutgoingSent) SetContactPK(pk []byte) {\n\tm.ContactPk = pk\n}\n\nfunc (m *AccountContactRequestIncomingDiscarded) SetContactPK(pk []byte) {\n\tm.ContactPk = pk\n}\n\nfunc (m *AccountContactRequestIncomingAccepted) SetContactPK(pk []byte) {\n\tm.ContactPk = pk\n}\n\nfunc (m *AccountContactBlocked) SetContactPK(pk []byte) {\n\tm.ContactPk = pk\n}\n\nfunc (m *AccountContactUnblocked) SetContactPK(pk []byte) {\n\tm.ContactPk = pk\n}\n\nfunc (m *AccountGroupLeft) SetGroupPK(pk []byte) {\n\tm.GroupPk = pk\n}\n\nfunc (m *ContactAliasKeyAdded) SetDevicePK(pk []byte) {\n\tm.DevicePk = pk\n}\n\nfunc (m *MultiMemberGroupAliasResolverAdded) SetDevicePK(pk []byte) {\n\tm.DevicePk = pk\n}\n\nfunc (m *MultiMemberGroupAdminRoleGranted) SetDevicePK(pk []byte) {\n\tm.DevicePk = pk\n}\n\nfunc (m *GroupMetadataPayloadSent) SetDevicePK(pk []byte) {\n\tm.DevicePk = pk\n}\n\nfunc (m *GroupReplicating) SetDevicePK(pk []byte) {\n\tm.DevicePk = pk\n}\n\nfunc (m *AccountVerifiedCredentialRegistered) SetDevicePK(pk []byte) {\n\tm.DevicePk = pk\n}\n"
  },
  {
    "path": "pkg/protocoltypes/example_test.go",
    "content": "package protocoltypes_test\n"
  },
  {
    "path": "pkg/protocoltypes/group.go",
    "content": "package protocoltypes\n\nimport (\n\tcrand \"crypto/rand\"\n\t\"encoding/hex\"\n\t\"io\"\n\n\t\"github.com/libp2p/go-libp2p/core/crypto\"\n\t\"golang.org/x/crypto/ed25519\"\n\t\"golang.org/x/crypto/hkdf\"\n\t\"golang.org/x/crypto/sha3\"\n\n\t\"berty.tech/weshnet/v2/pkg/cryptoutil\"\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n)\n\nfunc (m *Group) GetSigningPrivKey() (crypto.PrivKey, error) {\n\tif len(m.Secret) == 0 {\n\t\treturn nil, errcode.ErrCode_ErrMissingInput\n\t}\n\n\tedSK := ed25519.NewKeyFromSeed(m.Secret)\n\n\tsk, _, err := crypto.KeyPairFromStdKey(&edSK)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn sk, nil\n}\n\nfunc (m *Group) GetPubKey() (crypto.PubKey, error) {\n\treturn crypto.UnmarshalEd25519PublicKey(m.PublicKey)\n}\n\nfunc (m *Group) GetSigningPubKey() (crypto.PubKey, error) {\n\tif len(m.SignPub) != 0 {\n\t\treturn crypto.UnmarshalEd25519PublicKey(m.SignPub)\n\t}\n\n\tsk, err := m.GetSigningPrivKey()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn sk.GetPublic(), nil\n}\n\nfunc (m *Group) IsValid() error {\n\tpk, err := m.GetPubKey()\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\tok, err := pk.Verify(m.Secret, m.SecretSig)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrCryptoSignatureVerification.Wrap(err)\n\t}\n\n\tif !ok {\n\t\treturn errcode.ErrCode_ErrCryptoSignatureVerification\n\t}\n\n\treturn nil\n}\n\n// GroupIDAsString returns the group pub key as a string\nfunc (m *Group) GroupIDAsString() string {\n\treturn hex.EncodeToString(m.PublicKey)\n}\n\nfunc (m *Group) Copy() *Group {\n\treturn &Group{\n\t\tPublicKey: m.PublicKey,\n\t\tSecret:    m.Secret,\n\t\tSecretSig: m.SecretSig,\n\t\tGroupType: m.GroupType,\n\t\tSignPub:   m.SignPub,\n\t}\n}\n\nconst CurrentGroupVersion = 1\n\n// NewGroupMultiMember creates a new Group object and an invitation to be used by\n// the first member of the group\nfunc NewGroupMultiMember() (*Group, crypto.PrivKey, error) {\n\tpriv, pub, err := crypto.GenerateEd25519Key(crand.Reader)\n\tif err != nil {\n\t\treturn nil, nil, errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err)\n\t}\n\n\tpubBytes, err := pub.Raw()\n\tif err != nil {\n\t\treturn nil, nil, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\tsigning, _, err := crypto.GenerateEd25519Key(crand.Reader)\n\tif err != nil {\n\t\treturn nil, nil, errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err)\n\t}\n\n\tsigningBytes, err := cryptoutil.SeedFromEd25519PrivateKey(signing)\n\tif err != nil {\n\t\treturn nil, nil, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\tskSig, err := priv.Sign(signingBytes)\n\tif err != nil {\n\t\treturn nil, nil, errcode.ErrCode_ErrCryptoSignature.Wrap(err)\n\t}\n\n\tgroup := &Group{\n\t\tPublicKey: pubBytes,\n\t\tSecret:    signingBytes,\n\t\tSecretSig: skSig,\n\t\tGroupType: GroupType_GroupTypeMultiMember,\n\t}\n\n\tupdateKey, err := group.GetLinkKeyArray()\n\tif err != nil {\n\t\treturn nil, nil, errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err)\n\t}\n\n\tlinkKeySig, err := priv.Sign(updateKey[:])\n\tif err != nil {\n\t\treturn nil, nil, errcode.ErrCode_ErrCryptoSignature.Wrap(err)\n\t}\n\n\tgroup.LinkKeySig = linkKeySig\n\n\treturn group, priv, nil\n}\n\nfunc ComputeLinkKey(publicKey, secret []byte) (*[cryptoutil.KeySize]byte, error) {\n\tarr := [cryptoutil.KeySize]byte{}\n\n\tkdf := hkdf.New(sha3.New256, secret, nil, publicKey)\n\tif _, err := io.ReadFull(kdf, arr[:]); err != nil {\n\t\treturn nil, errcode.ErrCode_ErrStreamRead.Wrap(err)\n\t}\n\n\treturn &arr, nil\n}\n\nfunc (m *Group) GetLinkKeyArray() (*[cryptoutil.KeySize]byte, error) {\n\tif len(m.GetLinkKey()) == cryptoutil.KeySize {\n\t\tarr := [cryptoutil.KeySize]byte{}\n\n\t\tfor i, c := range m.GetLinkKey() {\n\t\t\tarr[i] = c\n\t\t}\n\n\t\treturn &arr, nil\n\t}\n\n\treturn ComputeLinkKey(m.GetPublicKey(), m.GetSecret())\n}\n\nfunc (m *Group) GetSharedSecret() *[cryptoutil.KeySize]byte {\n\tsharedSecret := [cryptoutil.KeySize]byte{}\n\tcopy(sharedSecret[:], m.GetSecret())\n\n\treturn &sharedSecret\n}\n"
  },
  {
    "path": "pkg/protocoltypes/protocoltypes.pb.go",
    "content": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.34.2\n// \tprotoc        (unknown)\n// source: protocoltypes.proto\n\npackage protocoltypes\n\nimport (\n\tprotoreflect \"google.golang.org/protobuf/reflect/protoreflect\"\n\tprotoimpl \"google.golang.org/protobuf/runtime/protoimpl\"\n\treflect \"reflect\"\n\tsync \"sync\"\n)\n\nconst (\n\t// Verify that this generated code is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)\n\t// Verify that runtime/protoimpl is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)\n)\n\ntype GroupType int32\n\nconst (\n\t// GroupTypeUndefined indicates that the value has not been set. For example, happens if group is replicated.\n\tGroupType_GroupTypeUndefined GroupType = 0\n\t// GroupTypeAccount is the group managing an account, available to all its devices.\n\tGroupType_GroupTypeAccount GroupType = 1\n\t// GroupTypeContact is the group created between two accounts, available to all their devices.\n\tGroupType_GroupTypeContact GroupType = 2\n\t// GroupTypeMultiMember is a group containing an undefined number of members.\n\tGroupType_GroupTypeMultiMember GroupType = 3\n)\n\n// Enum value maps for GroupType.\nvar (\n\tGroupType_name = map[int32]string{\n\t\t0: \"GroupTypeUndefined\",\n\t\t1: \"GroupTypeAccount\",\n\t\t2: \"GroupTypeContact\",\n\t\t3: \"GroupTypeMultiMember\",\n\t}\n\tGroupType_value = map[string]int32{\n\t\t\"GroupTypeUndefined\":   0,\n\t\t\"GroupTypeAccount\":     1,\n\t\t\"GroupTypeContact\":     2,\n\t\t\"GroupTypeMultiMember\": 3,\n\t}\n)\n\nfunc (x GroupType) Enum() *GroupType {\n\tp := new(GroupType)\n\t*p = x\n\treturn p\n}\n\nfunc (x GroupType) String() string {\n\treturn protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))\n}\n\nfunc (GroupType) Descriptor() protoreflect.EnumDescriptor {\n\treturn file_protocoltypes_proto_enumTypes[0].Descriptor()\n}\n\nfunc (GroupType) Type() protoreflect.EnumType {\n\treturn &file_protocoltypes_proto_enumTypes[0]\n}\n\nfunc (x GroupType) Number() protoreflect.EnumNumber {\n\treturn protoreflect.EnumNumber(x)\n}\n\n// Deprecated: Use GroupType.Descriptor instead.\nfunc (GroupType) EnumDescriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{0}\n}\n\ntype EventType int32\n\nconst (\n\t// EventTypeUndefined indicates that the value has not been set. Should not happen.\n\tEventType_EventTypeUndefined EventType = 0\n\t// EventTypeGroupMemberDeviceAdded indicates the payload includes that a member has added their device to the group\n\tEventType_EventTypeGroupMemberDeviceAdded EventType = 1\n\t// EventTypeGroupDeviceChainKeyAdded indicates the payload includes that a member has sent their device chain key to another member\n\tEventType_EventTypeGroupDeviceChainKeyAdded EventType = 2\n\t// EventTypeAccountGroupJoined indicates the payload includes that the account has joined a group\n\tEventType_EventTypeAccountGroupJoined EventType = 101\n\t// EventTypeAccountGroupLeft indicates the payload includes that the account has left a group\n\tEventType_EventTypeAccountGroupLeft EventType = 102\n\t// EventTypeAccountContactRequestDisabled indicates the payload includes that the account has disabled incoming contact requests\n\tEventType_EventTypeAccountContactRequestDisabled EventType = 103\n\t// EventTypeAccountContactRequestEnabled indicates the payload includes that the account has enabled incoming contact requests\n\tEventType_EventTypeAccountContactRequestEnabled EventType = 104\n\t// EventTypeAccountContactRequestReferenceReset indicates the payload includes that the account has a new contact request rendezvous seed\n\tEventType_EventTypeAccountContactRequestReferenceReset EventType = 105\n\t// EventTypeAccountContactRequestOutgoingEnqueued indicates the payload includes that the account will attempt to send a new contact request\n\tEventType_EventTypeAccountContactRequestOutgoingEnqueued EventType = 106\n\t// EventTypeAccountContactRequestOutgoingSent indicates the payload includes that the account has sent a contact request\n\tEventType_EventTypeAccountContactRequestOutgoingSent EventType = 107\n\t// EventTypeAccountContactRequestIncomingReceived indicates the payload includes that the account has received a contact request\n\tEventType_EventTypeAccountContactRequestIncomingReceived EventType = 108\n\t// EventTypeAccountContactRequestIncomingDiscarded indicates the payload includes that the account has ignored a contact request\n\tEventType_EventTypeAccountContactRequestIncomingDiscarded EventType = 109\n\t// EventTypeAccountContactRequestIncomingAccepted indicates the payload includes that the account has accepted a contact request\n\tEventType_EventTypeAccountContactRequestIncomingAccepted EventType = 110\n\t// EventTypeAccountContactBlocked indicates the payload includes that the account has blocked a contact\n\tEventType_EventTypeAccountContactBlocked EventType = 111\n\t// EventTypeAccountContactUnblocked indicates the payload includes that the account has unblocked a contact\n\tEventType_EventTypeAccountContactUnblocked EventType = 112\n\t// EventTypeContactAliasKeyAdded indicates the payload includes that the contact group has received an alias key\n\tEventType_EventTypeContactAliasKeyAdded EventType = 201\n\t// EventTypeMultiMemberGroupAliasResolverAdded indicates the payload includes that a member of the group sent their alias proof\n\tEventType_EventTypeMultiMemberGroupAliasResolverAdded EventType = 301\n\t// EventTypeMultiMemberGroupInitialMemberAnnounced indicates the payload includes that a member has authenticated themselves as the group owner\n\tEventType_EventTypeMultiMemberGroupInitialMemberAnnounced EventType = 302\n\t// EventTypeMultiMemberGroupAdminRoleGranted indicates the payload includes that an admin of the group granted another member as an admin\n\tEventType_EventTypeMultiMemberGroupAdminRoleGranted EventType = 303\n\t// EventTypeGroupReplicating indicates that the group has been registered for replication on a server\n\tEventType_EventTypeGroupReplicating EventType = 403\n\t// EventTypeAccountVerifiedCredentialRegistered\n\tEventType_EventTypeAccountVerifiedCredentialRegistered EventType = 500\n\t// EventTypeGroupMetadataPayloadSent indicates the payload includes an app specific event, unlike messages stored on the message store it is encrypted using a static key\n\tEventType_EventTypeGroupMetadataPayloadSent EventType = 1001\n)\n\n// Enum value maps for EventType.\nvar (\n\tEventType_name = map[int32]string{\n\t\t0:    \"EventTypeUndefined\",\n\t\t1:    \"EventTypeGroupMemberDeviceAdded\",\n\t\t2:    \"EventTypeGroupDeviceChainKeyAdded\",\n\t\t101:  \"EventTypeAccountGroupJoined\",\n\t\t102:  \"EventTypeAccountGroupLeft\",\n\t\t103:  \"EventTypeAccountContactRequestDisabled\",\n\t\t104:  \"EventTypeAccountContactRequestEnabled\",\n\t\t105:  \"EventTypeAccountContactRequestReferenceReset\",\n\t\t106:  \"EventTypeAccountContactRequestOutgoingEnqueued\",\n\t\t107:  \"EventTypeAccountContactRequestOutgoingSent\",\n\t\t108:  \"EventTypeAccountContactRequestIncomingReceived\",\n\t\t109:  \"EventTypeAccountContactRequestIncomingDiscarded\",\n\t\t110:  \"EventTypeAccountContactRequestIncomingAccepted\",\n\t\t111:  \"EventTypeAccountContactBlocked\",\n\t\t112:  \"EventTypeAccountContactUnblocked\",\n\t\t201:  \"EventTypeContactAliasKeyAdded\",\n\t\t301:  \"EventTypeMultiMemberGroupAliasResolverAdded\",\n\t\t302:  \"EventTypeMultiMemberGroupInitialMemberAnnounced\",\n\t\t303:  \"EventTypeMultiMemberGroupAdminRoleGranted\",\n\t\t403:  \"EventTypeGroupReplicating\",\n\t\t500:  \"EventTypeAccountVerifiedCredentialRegistered\",\n\t\t1001: \"EventTypeGroupMetadataPayloadSent\",\n\t}\n\tEventType_value = map[string]int32{\n\t\t\"EventTypeUndefined\":                              0,\n\t\t\"EventTypeGroupMemberDeviceAdded\":                 1,\n\t\t\"EventTypeGroupDeviceChainKeyAdded\":               2,\n\t\t\"EventTypeAccountGroupJoined\":                     101,\n\t\t\"EventTypeAccountGroupLeft\":                       102,\n\t\t\"EventTypeAccountContactRequestDisabled\":          103,\n\t\t\"EventTypeAccountContactRequestEnabled\":           104,\n\t\t\"EventTypeAccountContactRequestReferenceReset\":    105,\n\t\t\"EventTypeAccountContactRequestOutgoingEnqueued\":  106,\n\t\t\"EventTypeAccountContactRequestOutgoingSent\":      107,\n\t\t\"EventTypeAccountContactRequestIncomingReceived\":  108,\n\t\t\"EventTypeAccountContactRequestIncomingDiscarded\": 109,\n\t\t\"EventTypeAccountContactRequestIncomingAccepted\":  110,\n\t\t\"EventTypeAccountContactBlocked\":                  111,\n\t\t\"EventTypeAccountContactUnblocked\":                112,\n\t\t\"EventTypeContactAliasKeyAdded\":                   201,\n\t\t\"EventTypeMultiMemberGroupAliasResolverAdded\":     301,\n\t\t\"EventTypeMultiMemberGroupInitialMemberAnnounced\": 302,\n\t\t\"EventTypeMultiMemberGroupAdminRoleGranted\":       303,\n\t\t\"EventTypeGroupReplicating\":                       403,\n\t\t\"EventTypeAccountVerifiedCredentialRegistered\":    500,\n\t\t\"EventTypeGroupMetadataPayloadSent\":               1001,\n\t}\n)\n\nfunc (x EventType) Enum() *EventType {\n\tp := new(EventType)\n\t*p = x\n\treturn p\n}\n\nfunc (x EventType) String() string {\n\treturn protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))\n}\n\nfunc (EventType) Descriptor() protoreflect.EnumDescriptor {\n\treturn file_protocoltypes_proto_enumTypes[1].Descriptor()\n}\n\nfunc (EventType) Type() protoreflect.EnumType {\n\treturn &file_protocoltypes_proto_enumTypes[1]\n}\n\nfunc (x EventType) Number() protoreflect.EnumNumber {\n\treturn protoreflect.EnumNumber(x)\n}\n\n// Deprecated: Use EventType.Descriptor instead.\nfunc (EventType) EnumDescriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{1}\n}\n\ntype DebugInspectGroupLogType int32\n\nconst (\n\tDebugInspectGroupLogType_DebugInspectGroupLogTypeUndefined DebugInspectGroupLogType = 0\n\tDebugInspectGroupLogType_DebugInspectGroupLogTypeMessage   DebugInspectGroupLogType = 1\n\tDebugInspectGroupLogType_DebugInspectGroupLogTypeMetadata  DebugInspectGroupLogType = 2\n)\n\n// Enum value maps for DebugInspectGroupLogType.\nvar (\n\tDebugInspectGroupLogType_name = map[int32]string{\n\t\t0: \"DebugInspectGroupLogTypeUndefined\",\n\t\t1: \"DebugInspectGroupLogTypeMessage\",\n\t\t2: \"DebugInspectGroupLogTypeMetadata\",\n\t}\n\tDebugInspectGroupLogType_value = map[string]int32{\n\t\t\"DebugInspectGroupLogTypeUndefined\": 0,\n\t\t\"DebugInspectGroupLogTypeMessage\":   1,\n\t\t\"DebugInspectGroupLogTypeMetadata\":  2,\n\t}\n)\n\nfunc (x DebugInspectGroupLogType) Enum() *DebugInspectGroupLogType {\n\tp := new(DebugInspectGroupLogType)\n\t*p = x\n\treturn p\n}\n\nfunc (x DebugInspectGroupLogType) String() string {\n\treturn protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))\n}\n\nfunc (DebugInspectGroupLogType) Descriptor() protoreflect.EnumDescriptor {\n\treturn file_protocoltypes_proto_enumTypes[2].Descriptor()\n}\n\nfunc (DebugInspectGroupLogType) Type() protoreflect.EnumType {\n\treturn &file_protocoltypes_proto_enumTypes[2]\n}\n\nfunc (x DebugInspectGroupLogType) Number() protoreflect.EnumNumber {\n\treturn protoreflect.EnumNumber(x)\n}\n\n// Deprecated: Use DebugInspectGroupLogType.Descriptor instead.\nfunc (DebugInspectGroupLogType) EnumDescriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{2}\n}\n\ntype ContactState int32\n\nconst (\n\tContactState_ContactStateUndefined ContactState = 0\n\tContactState_ContactStateToRequest ContactState = 1\n\tContactState_ContactStateReceived  ContactState = 2\n\tContactState_ContactStateAdded     ContactState = 3\n\tContactState_ContactStateRemoved   ContactState = 4\n\tContactState_ContactStateDiscarded ContactState = 5\n\tContactState_ContactStateBlocked   ContactState = 6\n)\n\n// Enum value maps for ContactState.\nvar (\n\tContactState_name = map[int32]string{\n\t\t0: \"ContactStateUndefined\",\n\t\t1: \"ContactStateToRequest\",\n\t\t2: \"ContactStateReceived\",\n\t\t3: \"ContactStateAdded\",\n\t\t4: \"ContactStateRemoved\",\n\t\t5: \"ContactStateDiscarded\",\n\t\t6: \"ContactStateBlocked\",\n\t}\n\tContactState_value = map[string]int32{\n\t\t\"ContactStateUndefined\": 0,\n\t\t\"ContactStateToRequest\": 1,\n\t\t\"ContactStateReceived\":  2,\n\t\t\"ContactStateAdded\":     3,\n\t\t\"ContactStateRemoved\":   4,\n\t\t\"ContactStateDiscarded\": 5,\n\t\t\"ContactStateBlocked\":   6,\n\t}\n)\n\nfunc (x ContactState) Enum() *ContactState {\n\tp := new(ContactState)\n\t*p = x\n\treturn p\n}\n\nfunc (x ContactState) String() string {\n\treturn protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))\n}\n\nfunc (ContactState) Descriptor() protoreflect.EnumDescriptor {\n\treturn file_protocoltypes_proto_enumTypes[3].Descriptor()\n}\n\nfunc (ContactState) Type() protoreflect.EnumType {\n\treturn &file_protocoltypes_proto_enumTypes[3]\n}\n\nfunc (x ContactState) Number() protoreflect.EnumNumber {\n\treturn protoreflect.EnumNumber(x)\n}\n\n// Deprecated: Use ContactState.Descriptor instead.\nfunc (ContactState) EnumDescriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{3}\n}\n\ntype Direction int32\n\nconst (\n\tDirection_UnknownDir  Direction = 0\n\tDirection_InboundDir  Direction = 1\n\tDirection_OutboundDir Direction = 2\n\tDirection_BiDir       Direction = 3\n)\n\n// Enum value maps for Direction.\nvar (\n\tDirection_name = map[int32]string{\n\t\t0: \"UnknownDir\",\n\t\t1: \"InboundDir\",\n\t\t2: \"OutboundDir\",\n\t\t3: \"BiDir\",\n\t}\n\tDirection_value = map[string]int32{\n\t\t\"UnknownDir\":  0,\n\t\t\"InboundDir\":  1,\n\t\t\"OutboundDir\": 2,\n\t\t\"BiDir\":       3,\n\t}\n)\n\nfunc (x Direction) Enum() *Direction {\n\tp := new(Direction)\n\t*p = x\n\treturn p\n}\n\nfunc (x Direction) String() string {\n\treturn protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))\n}\n\nfunc (Direction) Descriptor() protoreflect.EnumDescriptor {\n\treturn file_protocoltypes_proto_enumTypes[4].Descriptor()\n}\n\nfunc (Direction) Type() protoreflect.EnumType {\n\treturn &file_protocoltypes_proto_enumTypes[4]\n}\n\nfunc (x Direction) Number() protoreflect.EnumNumber {\n\treturn protoreflect.EnumNumber(x)\n}\n\n// Deprecated: Use Direction.Descriptor instead.\nfunc (Direction) EnumDescriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{4}\n}\n\ntype ServiceGetConfiguration_SettingState int32\n\nconst (\n\tServiceGetConfiguration_Unknown     ServiceGetConfiguration_SettingState = 0\n\tServiceGetConfiguration_Enabled     ServiceGetConfiguration_SettingState = 1\n\tServiceGetConfiguration_Disabled    ServiceGetConfiguration_SettingState = 2\n\tServiceGetConfiguration_Unavailable ServiceGetConfiguration_SettingState = 3\n)\n\n// Enum value maps for ServiceGetConfiguration_SettingState.\nvar (\n\tServiceGetConfiguration_SettingState_name = map[int32]string{\n\t\t0: \"Unknown\",\n\t\t1: \"Enabled\",\n\t\t2: \"Disabled\",\n\t\t3: \"Unavailable\",\n\t}\n\tServiceGetConfiguration_SettingState_value = map[string]int32{\n\t\t\"Unknown\":     0,\n\t\t\"Enabled\":     1,\n\t\t\"Disabled\":    2,\n\t\t\"Unavailable\": 3,\n\t}\n)\n\nfunc (x ServiceGetConfiguration_SettingState) Enum() *ServiceGetConfiguration_SettingState {\n\tp := new(ServiceGetConfiguration_SettingState)\n\t*p = x\n\treturn p\n}\n\nfunc (x ServiceGetConfiguration_SettingState) String() string {\n\treturn protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))\n}\n\nfunc (ServiceGetConfiguration_SettingState) Descriptor() protoreflect.EnumDescriptor {\n\treturn file_protocoltypes_proto_enumTypes[5].Descriptor()\n}\n\nfunc (ServiceGetConfiguration_SettingState) Type() protoreflect.EnumType {\n\treturn &file_protocoltypes_proto_enumTypes[5]\n}\n\nfunc (x ServiceGetConfiguration_SettingState) Number() protoreflect.EnumNumber {\n\treturn protoreflect.EnumNumber(x)\n}\n\n// Deprecated: Use ServiceGetConfiguration_SettingState.Descriptor instead.\nfunc (ServiceGetConfiguration_SettingState) EnumDescriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{34, 0}\n}\n\ntype GroupDeviceStatus_Type int32\n\nconst (\n\tGroupDeviceStatus_TypeUnknown          GroupDeviceStatus_Type = 0\n\tGroupDeviceStatus_TypePeerDisconnected GroupDeviceStatus_Type = 1\n\tGroupDeviceStatus_TypePeerConnected    GroupDeviceStatus_Type = 2\n\tGroupDeviceStatus_TypePeerReconnecting GroupDeviceStatus_Type = 3\n)\n\n// Enum value maps for GroupDeviceStatus_Type.\nvar (\n\tGroupDeviceStatus_Type_name = map[int32]string{\n\t\t0: \"TypeUnknown\",\n\t\t1: \"TypePeerDisconnected\",\n\t\t2: \"TypePeerConnected\",\n\t\t3: \"TypePeerReconnecting\",\n\t}\n\tGroupDeviceStatus_Type_value = map[string]int32{\n\t\t\"TypeUnknown\":          0,\n\t\t\"TypePeerDisconnected\": 1,\n\t\t\"TypePeerConnected\":    2,\n\t\t\"TypePeerReconnecting\": 3,\n\t}\n)\n\nfunc (x GroupDeviceStatus_Type) Enum() *GroupDeviceStatus_Type {\n\tp := new(GroupDeviceStatus_Type)\n\t*p = x\n\treturn p\n}\n\nfunc (x GroupDeviceStatus_Type) String() string {\n\treturn protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))\n}\n\nfunc (GroupDeviceStatus_Type) Descriptor() protoreflect.EnumDescriptor {\n\treturn file_protocoltypes_proto_enumTypes[6].Descriptor()\n}\n\nfunc (GroupDeviceStatus_Type) Type() protoreflect.EnumType {\n\treturn &file_protocoltypes_proto_enumTypes[6]\n}\n\nfunc (x GroupDeviceStatus_Type) Number() protoreflect.EnumNumber {\n\treturn protoreflect.EnumNumber(x)\n}\n\n// Deprecated: Use GroupDeviceStatus_Type.Descriptor instead.\nfunc (GroupDeviceStatus_Type) EnumDescriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{62, 0}\n}\n\ntype GroupDeviceStatus_Transport int32\n\nconst (\n\tGroupDeviceStatus_TptUnknown   GroupDeviceStatus_Transport = 0\n\tGroupDeviceStatus_TptLAN       GroupDeviceStatus_Transport = 1\n\tGroupDeviceStatus_TptWAN       GroupDeviceStatus_Transport = 2\n\tGroupDeviceStatus_TptProximity GroupDeviceStatus_Transport = 3\n)\n\n// Enum value maps for GroupDeviceStatus_Transport.\nvar (\n\tGroupDeviceStatus_Transport_name = map[int32]string{\n\t\t0: \"TptUnknown\",\n\t\t1: \"TptLAN\",\n\t\t2: \"TptWAN\",\n\t\t3: \"TptProximity\",\n\t}\n\tGroupDeviceStatus_Transport_value = map[string]int32{\n\t\t\"TptUnknown\":   0,\n\t\t\"TptLAN\":       1,\n\t\t\"TptWAN\":       2,\n\t\t\"TptProximity\": 3,\n\t}\n)\n\nfunc (x GroupDeviceStatus_Transport) Enum() *GroupDeviceStatus_Transport {\n\tp := new(GroupDeviceStatus_Transport)\n\t*p = x\n\treturn p\n}\n\nfunc (x GroupDeviceStatus_Transport) String() string {\n\treturn protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))\n}\n\nfunc (GroupDeviceStatus_Transport) Descriptor() protoreflect.EnumDescriptor {\n\treturn file_protocoltypes_proto_enumTypes[7].Descriptor()\n}\n\nfunc (GroupDeviceStatus_Transport) Type() protoreflect.EnumType {\n\treturn &file_protocoltypes_proto_enumTypes[7]\n}\n\nfunc (x GroupDeviceStatus_Transport) Number() protoreflect.EnumNumber {\n\treturn protoreflect.EnumNumber(x)\n}\n\n// Deprecated: Use GroupDeviceStatus_Transport.Descriptor instead.\nfunc (GroupDeviceStatus_Transport) EnumDescriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{62, 1}\n}\n\ntype PeerList_Feature int32\n\nconst (\n\tPeerList_UnknownFeature PeerList_Feature = 0\n\tPeerList_WeshFeature    PeerList_Feature = 1\n\tPeerList_BLEFeature     PeerList_Feature = 2\n\tPeerList_LocalFeature   PeerList_Feature = 3\n\tPeerList_TorFeature     PeerList_Feature = 4\n\tPeerList_QuicFeature    PeerList_Feature = 5\n)\n\n// Enum value maps for PeerList_Feature.\nvar (\n\tPeerList_Feature_name = map[int32]string{\n\t\t0: \"UnknownFeature\",\n\t\t1: \"WeshFeature\",\n\t\t2: \"BLEFeature\",\n\t\t3: \"LocalFeature\",\n\t\t4: \"TorFeature\",\n\t\t5: \"QuicFeature\",\n\t}\n\tPeerList_Feature_value = map[string]int32{\n\t\t\"UnknownFeature\": 0,\n\t\t\"WeshFeature\":    1,\n\t\t\"BLEFeature\":     2,\n\t\t\"LocalFeature\":   3,\n\t\t\"TorFeature\":     4,\n\t\t\"QuicFeature\":    5,\n\t}\n)\n\nfunc (x PeerList_Feature) Enum() *PeerList_Feature {\n\tp := new(PeerList_Feature)\n\t*p = x\n\treturn p\n}\n\nfunc (x PeerList_Feature) String() string {\n\treturn protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))\n}\n\nfunc (PeerList_Feature) Descriptor() protoreflect.EnumDescriptor {\n\treturn file_protocoltypes_proto_enumTypes[8].Descriptor()\n}\n\nfunc (PeerList_Feature) Type() protoreflect.EnumType {\n\treturn &file_protocoltypes_proto_enumTypes[8]\n}\n\nfunc (x PeerList_Feature) Number() protoreflect.EnumNumber {\n\treturn protoreflect.EnumNumber(x)\n}\n\n// Deprecated: Use PeerList_Feature.Descriptor instead.\nfunc (PeerList_Feature) EnumDescriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{75, 0}\n}\n\n// Account describes all the secrets that identifies an Account\ntype Account struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// group specifies which group is used to manage the account\n\tGroup *Group `protobuf:\"bytes,1,opt,name=group,proto3\" json:\"group,omitempty\"`\n\t// account_private_key, private part is used to signs handshake, signs device, create contacts group keys via ECDH -- public part is used to have a shareable identity\n\tAccountPrivateKey []byte `protobuf:\"bytes,2,opt,name=account_private_key,json=accountPrivateKey,proto3\" json:\"account_private_key,omitempty\"`\n\t// alias_private_key, private part is use to derive group members private keys, signs alias proofs, public part can be shared to contacts to prove identity\n\tAliasPrivateKey []byte `protobuf:\"bytes,3,opt,name=alias_private_key,json=aliasPrivateKey,proto3\" json:\"alias_private_key,omitempty\"`\n\t// public_rendezvous_seed, rendezvous seed used for direct communication\n\tPublicRendezvousSeed []byte `protobuf:\"bytes,4,opt,name=public_rendezvous_seed,json=publicRendezvousSeed,proto3\" json:\"public_rendezvous_seed,omitempty\"`\n}\n\nfunc (x *Account) Reset() {\n\t*x = Account{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[0]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *Account) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Account) ProtoMessage() {}\n\nfunc (x *Account) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[0]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use Account.ProtoReflect.Descriptor instead.\nfunc (*Account) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *Account) GetGroup() *Group {\n\tif x != nil {\n\t\treturn x.Group\n\t}\n\treturn nil\n}\n\nfunc (x *Account) GetAccountPrivateKey() []byte {\n\tif x != nil {\n\t\treturn x.AccountPrivateKey\n\t}\n\treturn nil\n}\n\nfunc (x *Account) GetAliasPrivateKey() []byte {\n\tif x != nil {\n\t\treturn x.AliasPrivateKey\n\t}\n\treturn nil\n}\n\nfunc (x *Account) GetPublicRendezvousSeed() []byte {\n\tif x != nil {\n\t\treturn x.PublicRendezvousSeed\n\t}\n\treturn nil\n}\n\n// Group define a group and is enough to invite someone to it\ntype Group struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// public_key is the identifier of the group, it signs the group secret and the initial member of a multi-member group\n\tPublicKey []byte `protobuf:\"bytes,1,opt,name=public_key,json=publicKey,proto3\" json:\"public_key,omitempty\"`\n\t// secret is the symmetric secret of the group, which is used to encrypt the metadata\n\tSecret []byte `protobuf:\"bytes,2,opt,name=secret,proto3\" json:\"secret,omitempty\"`\n\t// secret_sig is the signature of the secret used to ensure the validity of the group\n\tSecretSig []byte `protobuf:\"bytes,3,opt,name=secret_sig,json=secretSig,proto3\" json:\"secret_sig,omitempty\"`\n\t// group_type specifies the type of the group, used to determine how device chain key is generated\n\tGroupType GroupType `protobuf:\"varint,4,opt,name=group_type,json=groupType,proto3,enum=weshnet.protocol.v1.GroupType\" json:\"group_type,omitempty\"`\n\t// sign_pub is the signature public key used to verify entries, not required when secret and secret_sig are provided\n\tSignPub []byte `protobuf:\"bytes,5,opt,name=sign_pub,json=signPub,proto3\" json:\"sign_pub,omitempty\"`\n\t// link_key is the secret key used to exchange group updates and links to attachments, useful for replication services\n\tLinkKey []byte `protobuf:\"bytes,6,opt,name=link_key,json=linkKey,proto3\" json:\"link_key,omitempty\"`\n\t// link_key_sig is the signature of the link_key using the group private key\n\tLinkKeySig []byte `protobuf:\"bytes,7,opt,name=link_key_sig,json=linkKeySig,proto3\" json:\"link_key_sig,omitempty\"`\n}\n\nfunc (x *Group) Reset() {\n\t*x = Group{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[1]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *Group) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Group) ProtoMessage() {}\n\nfunc (x *Group) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[1]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use Group.ProtoReflect.Descriptor instead.\nfunc (*Group) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{1}\n}\n\nfunc (x *Group) GetPublicKey() []byte {\n\tif x != nil {\n\t\treturn x.PublicKey\n\t}\n\treturn nil\n}\n\nfunc (x *Group) GetSecret() []byte {\n\tif x != nil {\n\t\treturn x.Secret\n\t}\n\treturn nil\n}\n\nfunc (x *Group) GetSecretSig() []byte {\n\tif x != nil {\n\t\treturn x.SecretSig\n\t}\n\treturn nil\n}\n\nfunc (x *Group) GetGroupType() GroupType {\n\tif x != nil {\n\t\treturn x.GroupType\n\t}\n\treturn GroupType_GroupTypeUndefined\n}\n\nfunc (x *Group) GetSignPub() []byte {\n\tif x != nil {\n\t\treturn x.SignPub\n\t}\n\treturn nil\n}\n\nfunc (x *Group) GetLinkKey() []byte {\n\tif x != nil {\n\t\treturn x.LinkKey\n\t}\n\treturn nil\n}\n\nfunc (x *Group) GetLinkKeySig() []byte {\n\tif x != nil {\n\t\treturn x.LinkKeySig\n\t}\n\treturn nil\n}\n\ntype GroupHeadsExport struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// public_key is the identifier of the group, it signs the group secret and the initial member of a multi-member group\n\tPublicKey []byte `protobuf:\"bytes,1,opt,name=public_key,json=publicKey,proto3\" json:\"public_key,omitempty\"`\n\t// sign_pub is the signature public key used to verify entries\n\tSignPub []byte `protobuf:\"bytes,2,opt,name=sign_pub,json=signPub,proto3\" json:\"sign_pub,omitempty\"`\n\t// metadata_heads_cids are the heads of the metadata store that should be restored from an export\n\tMetadataHeadsCids [][]byte `protobuf:\"bytes,3,rep,name=metadata_heads_cids,json=metadataHeadsCids,proto3\" json:\"metadata_heads_cids,omitempty\"`\n\t// messages_heads_cids are the heads of the metadata store that should be restored from an export\n\tMessagesHeadsCids [][]byte `protobuf:\"bytes,4,rep,name=messages_heads_cids,json=messagesHeadsCids,proto3\" json:\"messages_heads_cids,omitempty\"`\n\t// link_key\n\tLinkKey []byte `protobuf:\"bytes,5,opt,name=link_key,json=linkKey,proto3\" json:\"link_key,omitempty\"`\n}\n\nfunc (x *GroupHeadsExport) Reset() {\n\t*x = GroupHeadsExport{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[2]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *GroupHeadsExport) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*GroupHeadsExport) ProtoMessage() {}\n\nfunc (x *GroupHeadsExport) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[2]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use GroupHeadsExport.ProtoReflect.Descriptor instead.\nfunc (*GroupHeadsExport) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{2}\n}\n\nfunc (x *GroupHeadsExport) GetPublicKey() []byte {\n\tif x != nil {\n\t\treturn x.PublicKey\n\t}\n\treturn nil\n}\n\nfunc (x *GroupHeadsExport) GetSignPub() []byte {\n\tif x != nil {\n\t\treturn x.SignPub\n\t}\n\treturn nil\n}\n\nfunc (x *GroupHeadsExport) GetMetadataHeadsCids() [][]byte {\n\tif x != nil {\n\t\treturn x.MetadataHeadsCids\n\t}\n\treturn nil\n}\n\nfunc (x *GroupHeadsExport) GetMessagesHeadsCids() [][]byte {\n\tif x != nil {\n\t\treturn x.MessagesHeadsCids\n\t}\n\treturn nil\n}\n\nfunc (x *GroupHeadsExport) GetLinkKey() []byte {\n\tif x != nil {\n\t\treturn x.LinkKey\n\t}\n\treturn nil\n}\n\n// GroupMetadata is used in GroupEnvelope and only readable by invited group members\ntype GroupMetadata struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// event_type defines which event type is used\n\tEventType EventType `protobuf:\"varint,1,opt,name=event_type,json=eventType,proto3,enum=weshnet.protocol.v1.EventType\" json:\"event_type,omitempty\"`\n\t// the serialization depends on event_type, event is symmetrically encrypted\n\tPayload []byte `protobuf:\"bytes,2,opt,name=payload,proto3\" json:\"payload,omitempty\"`\n\t// sig is the signature of the payload, it depends on the event_type for the used key\n\tSig []byte `protobuf:\"bytes,3,opt,name=sig,proto3\" json:\"sig,omitempty\"`\n\t// protocol_metadata is protocol layer data\n\tProtocolMetadata *ProtocolMetadata `protobuf:\"bytes,4,opt,name=protocol_metadata,json=protocolMetadata,proto3\" json:\"protocol_metadata,omitempty\"`\n}\n\nfunc (x *GroupMetadata) Reset() {\n\t*x = GroupMetadata{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[3]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *GroupMetadata) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*GroupMetadata) ProtoMessage() {}\n\nfunc (x *GroupMetadata) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[3]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use GroupMetadata.ProtoReflect.Descriptor instead.\nfunc (*GroupMetadata) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{3}\n}\n\nfunc (x *GroupMetadata) GetEventType() EventType {\n\tif x != nil {\n\t\treturn x.EventType\n\t}\n\treturn EventType_EventTypeUndefined\n}\n\nfunc (x *GroupMetadata) GetPayload() []byte {\n\tif x != nil {\n\t\treturn x.Payload\n\t}\n\treturn nil\n}\n\nfunc (x *GroupMetadata) GetSig() []byte {\n\tif x != nil {\n\t\treturn x.Sig\n\t}\n\treturn nil\n}\n\nfunc (x *GroupMetadata) GetProtocolMetadata() *ProtocolMetadata {\n\tif x != nil {\n\t\treturn x.ProtocolMetadata\n\t}\n\treturn nil\n}\n\n// GroupEnvelope is a publicly exposed structure containing a group metadata event\ntype GroupEnvelope struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// nonce is used to encrypt the message\n\tNonce []byte `protobuf:\"bytes,1,opt,name=nonce,proto3\" json:\"nonce,omitempty\"`\n\t// event is encrypted using a symmetric key shared among group members\n\tEvent []byte `protobuf:\"bytes,2,opt,name=event,proto3\" json:\"event,omitempty\"`\n}\n\nfunc (x *GroupEnvelope) Reset() {\n\t*x = GroupEnvelope{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[4]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *GroupEnvelope) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*GroupEnvelope) ProtoMessage() {}\n\nfunc (x *GroupEnvelope) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[4]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use GroupEnvelope.ProtoReflect.Descriptor instead.\nfunc (*GroupEnvelope) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{4}\n}\n\nfunc (x *GroupEnvelope) GetNonce() []byte {\n\tif x != nil {\n\t\treturn x.Nonce\n\t}\n\treturn nil\n}\n\nfunc (x *GroupEnvelope) GetEvent() []byte {\n\tif x != nil {\n\t\treturn x.Event\n\t}\n\treturn nil\n}\n\n// MessageHeaders is used in MessageEnvelope and only readable by invited group members\ntype MessageHeaders struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// counter is the current counter value for the specified device\n\tCounter uint64 `protobuf:\"varint,1,opt,name=counter,proto3\" json:\"counter,omitempty\"`\n\t// device_pk is the public key of the device sending the message\n\tDevicePk []byte `protobuf:\"bytes,2,opt,name=device_pk,json=devicePk,proto3\" json:\"device_pk,omitempty\"`\n\t// sig is the signature of the encrypted message using the device's private key\n\tSig []byte `protobuf:\"bytes,3,opt,name=sig,proto3\" json:\"sig,omitempty\"`\n\t// metadata allow to pass custom informations\n\tMetadata map[string]string `protobuf:\"bytes,4,rep,name=metadata,proto3\" json:\"metadata,omitempty\" protobuf_key:\"bytes,1,opt,name=key,proto3\" protobuf_val:\"bytes,2,opt,name=value,proto3\"`\n}\n\nfunc (x *MessageHeaders) Reset() {\n\t*x = MessageHeaders{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[5]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *MessageHeaders) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*MessageHeaders) ProtoMessage() {}\n\nfunc (x *MessageHeaders) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[5]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use MessageHeaders.ProtoReflect.Descriptor instead.\nfunc (*MessageHeaders) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{5}\n}\n\nfunc (x *MessageHeaders) GetCounter() uint64 {\n\tif x != nil {\n\t\treturn x.Counter\n\t}\n\treturn 0\n}\n\nfunc (x *MessageHeaders) GetDevicePk() []byte {\n\tif x != nil {\n\t\treturn x.DevicePk\n\t}\n\treturn nil\n}\n\nfunc (x *MessageHeaders) GetSig() []byte {\n\tif x != nil {\n\t\treturn x.Sig\n\t}\n\treturn nil\n}\n\nfunc (x *MessageHeaders) GetMetadata() map[string]string {\n\tif x != nil {\n\t\treturn x.Metadata\n\t}\n\treturn nil\n}\n\ntype ProtocolMetadata struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *ProtocolMetadata) Reset() {\n\t*x = ProtocolMetadata{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[6]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ProtocolMetadata) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ProtocolMetadata) ProtoMessage() {}\n\nfunc (x *ProtocolMetadata) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[6]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ProtocolMetadata.ProtoReflect.Descriptor instead.\nfunc (*ProtocolMetadata) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{6}\n}\n\n// EncryptedMessage is used in MessageEnvelope and only readable by groups members that joined before the message was sent\ntype EncryptedMessage struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// plaintext is the app layer data\n\tPlaintext []byte `protobuf:\"bytes,1,opt,name=plaintext,proto3\" json:\"plaintext,omitempty\"`\n\t// protocol_metadata is protocol layer data\n\tProtocolMetadata *ProtocolMetadata `protobuf:\"bytes,2,opt,name=protocol_metadata,json=protocolMetadata,proto3\" json:\"protocol_metadata,omitempty\"`\n}\n\nfunc (x *EncryptedMessage) Reset() {\n\t*x = EncryptedMessage{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[7]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *EncryptedMessage) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*EncryptedMessage) ProtoMessage() {}\n\nfunc (x *EncryptedMessage) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[7]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use EncryptedMessage.ProtoReflect.Descriptor instead.\nfunc (*EncryptedMessage) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{7}\n}\n\nfunc (x *EncryptedMessage) GetPlaintext() []byte {\n\tif x != nil {\n\t\treturn x.Plaintext\n\t}\n\treturn nil\n}\n\nfunc (x *EncryptedMessage) GetProtocolMetadata() *ProtocolMetadata {\n\tif x != nil {\n\t\treturn x.ProtocolMetadata\n\t}\n\treturn nil\n}\n\n// MessageEnvelope is a publicly exposed structure containing a group secure message\ntype MessageEnvelope struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// message_headers is an encrypted serialization using a symmetric key of a MessageHeaders message\n\tMessageHeaders []byte `protobuf:\"bytes,1,opt,name=message_headers,json=messageHeaders,proto3\" json:\"message_headers,omitempty\"`\n\t// message is an encrypted message, only readable by group members who previously received the appropriate chain key\n\tMessage []byte `protobuf:\"bytes,2,opt,name=message,proto3\" json:\"message,omitempty\"`\n\t// nonce is a nonce for message headers\n\tNonce []byte `protobuf:\"bytes,3,opt,name=nonce,proto3\" json:\"nonce,omitempty\"`\n}\n\nfunc (x *MessageEnvelope) Reset() {\n\t*x = MessageEnvelope{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[8]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *MessageEnvelope) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*MessageEnvelope) ProtoMessage() {}\n\nfunc (x *MessageEnvelope) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[8]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use MessageEnvelope.ProtoReflect.Descriptor instead.\nfunc (*MessageEnvelope) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{8}\n}\n\nfunc (x *MessageEnvelope) GetMessageHeaders() []byte {\n\tif x != nil {\n\t\treturn x.MessageHeaders\n\t}\n\treturn nil\n}\n\nfunc (x *MessageEnvelope) GetMessage() []byte {\n\tif x != nil {\n\t\treturn x.Message\n\t}\n\treturn nil\n}\n\nfunc (x *MessageEnvelope) GetNonce() []byte {\n\tif x != nil {\n\t\treturn x.Nonce\n\t}\n\treturn nil\n}\n\n// EventContext adds context (its id, its parents and its attachments) to an event\ntype EventContext struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// id is the CID of the underlying OrbitDB event\n\tId []byte `protobuf:\"bytes,1,opt,name=id,proto3\" json:\"id,omitempty\"`\n\t// id are the the CIDs of the underlying parents of the OrbitDB event\n\tParentIds [][]byte `protobuf:\"bytes,2,rep,name=parent_ids,json=parentIds,proto3\" json:\"parent_ids,omitempty\"`\n\t// group_pk receiving the event\n\tGroupPk []byte `protobuf:\"bytes,3,opt,name=group_pk,json=groupPk,proto3\" json:\"group_pk,omitempty\"`\n}\n\nfunc (x *EventContext) Reset() {\n\t*x = EventContext{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[9]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *EventContext) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*EventContext) ProtoMessage() {}\n\nfunc (x *EventContext) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[9]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use EventContext.ProtoReflect.Descriptor instead.\nfunc (*EventContext) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{9}\n}\n\nfunc (x *EventContext) GetId() []byte {\n\tif x != nil {\n\t\treturn x.Id\n\t}\n\treturn nil\n}\n\nfunc (x *EventContext) GetParentIds() [][]byte {\n\tif x != nil {\n\t\treturn x.ParentIds\n\t}\n\treturn nil\n}\n\nfunc (x *EventContext) GetGroupPk() []byte {\n\tif x != nil {\n\t\treturn x.GroupPk\n\t}\n\treturn nil\n}\n\n// GroupMetadataPayloadSent is an app defined message, accessible to future group members\ntype GroupMetadataPayloadSent struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// device_pk is the device sending the event, signs the message\n\tDevicePk []byte `protobuf:\"bytes,1,opt,name=device_pk,json=devicePk,proto3\" json:\"device_pk,omitempty\"`\n\t// message is the payload\n\tMessage []byte `protobuf:\"bytes,2,opt,name=message,proto3\" json:\"message,omitempty\"`\n}\n\nfunc (x *GroupMetadataPayloadSent) Reset() {\n\t*x = GroupMetadataPayloadSent{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[10]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *GroupMetadataPayloadSent) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*GroupMetadataPayloadSent) ProtoMessage() {}\n\nfunc (x *GroupMetadataPayloadSent) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[10]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use GroupMetadataPayloadSent.ProtoReflect.Descriptor instead.\nfunc (*GroupMetadataPayloadSent) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{10}\n}\n\nfunc (x *GroupMetadataPayloadSent) GetDevicePk() []byte {\n\tif x != nil {\n\t\treturn x.DevicePk\n\t}\n\treturn nil\n}\n\nfunc (x *GroupMetadataPayloadSent) GetMessage() []byte {\n\tif x != nil {\n\t\treturn x.Message\n\t}\n\treturn nil\n}\n\n// ContactAliasKeyAdded is an event type where ones shares their alias public key\ntype ContactAliasKeyAdded struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// device_pk is the device sending the event, signs the message\n\tDevicePk []byte `protobuf:\"bytes,1,opt,name=device_pk,json=devicePk,proto3\" json:\"device_pk,omitempty\"`\n\t// alias_pk is the alias key which will be used to verify a contact identity\n\tAliasPk []byte `protobuf:\"bytes,2,opt,name=alias_pk,json=aliasPk,proto3\" json:\"alias_pk,omitempty\"`\n}\n\nfunc (x *ContactAliasKeyAdded) Reset() {\n\t*x = ContactAliasKeyAdded{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[11]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ContactAliasKeyAdded) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ContactAliasKeyAdded) ProtoMessage() {}\n\nfunc (x *ContactAliasKeyAdded) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[11]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ContactAliasKeyAdded.ProtoReflect.Descriptor instead.\nfunc (*ContactAliasKeyAdded) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{11}\n}\n\nfunc (x *ContactAliasKeyAdded) GetDevicePk() []byte {\n\tif x != nil {\n\t\treturn x.DevicePk\n\t}\n\treturn nil\n}\n\nfunc (x *ContactAliasKeyAdded) GetAliasPk() []byte {\n\tif x != nil {\n\t\treturn x.AliasPk\n\t}\n\treturn nil\n}\n\n// GroupMemberDeviceAdded is an event which indicates to a group a new device (and eventually a new member) is joining it\n// When added on AccountGroup, this event should be followed by appropriate GroupMemberDeviceAdded and GroupDeviceChainKeyAdded events\ntype GroupMemberDeviceAdded struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// member_pk is the member sending the event\n\tMemberPk []byte `protobuf:\"bytes,1,opt,name=member_pk,json=memberPk,proto3\" json:\"member_pk,omitempty\"`\n\t// device_pk is the device sending the event, signs the message\n\tDevicePk []byte `protobuf:\"bytes,2,opt,name=device_pk,json=devicePk,proto3\" json:\"device_pk,omitempty\"`\n\t// member_sig is used to prove the ownership of the member pk\n\tMemberSig []byte `protobuf:\"bytes,3,opt,name=member_sig,json=memberSig,proto3\" json:\"member_sig,omitempty\"` // TODO: signature of what ??? ensure it can't be replayed\n}\n\nfunc (x *GroupMemberDeviceAdded) Reset() {\n\t*x = GroupMemberDeviceAdded{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[12]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *GroupMemberDeviceAdded) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*GroupMemberDeviceAdded) ProtoMessage() {}\n\nfunc (x *GroupMemberDeviceAdded) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[12]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use GroupMemberDeviceAdded.ProtoReflect.Descriptor instead.\nfunc (*GroupMemberDeviceAdded) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{12}\n}\n\nfunc (x *GroupMemberDeviceAdded) GetMemberPk() []byte {\n\tif x != nil {\n\t\treturn x.MemberPk\n\t}\n\treturn nil\n}\n\nfunc (x *GroupMemberDeviceAdded) GetDevicePk() []byte {\n\tif x != nil {\n\t\treturn x.DevicePk\n\t}\n\treturn nil\n}\n\nfunc (x *GroupMemberDeviceAdded) GetMemberSig() []byte {\n\tif x != nil {\n\t\treturn x.MemberSig\n\t}\n\treturn nil\n}\n\n// DeviceChainKey is a chain key, which will be encrypted for a specific member of the group\ntype DeviceChainKey struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// chain_key is the current value of the chain key of the group device\n\tChainKey []byte `protobuf:\"bytes,1,opt,name=chain_key,json=chainKey,proto3\" json:\"chain_key,omitempty\"`\n\t// counter is the current value of the counter of the group device\n\tCounter uint64 `protobuf:\"varint,2,opt,name=counter,proto3\" json:\"counter,omitempty\"`\n}\n\nfunc (x *DeviceChainKey) Reset() {\n\t*x = DeviceChainKey{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[13]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *DeviceChainKey) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*DeviceChainKey) ProtoMessage() {}\n\nfunc (x *DeviceChainKey) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[13]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use DeviceChainKey.ProtoReflect.Descriptor instead.\nfunc (*DeviceChainKey) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{13}\n}\n\nfunc (x *DeviceChainKey) GetChainKey() []byte {\n\tif x != nil {\n\t\treturn x.ChainKey\n\t}\n\treturn nil\n}\n\nfunc (x *DeviceChainKey) GetCounter() uint64 {\n\tif x != nil {\n\t\treturn x.Counter\n\t}\n\treturn 0\n}\n\n// GroupDeviceChainKeyAdded is an event which indicates to a group member a device chain key\ntype GroupDeviceChainKeyAdded struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// device_pk is the device sending the event, signs the message\n\tDevicePk []byte `protobuf:\"bytes,1,opt,name=device_pk,json=devicePk,proto3\" json:\"device_pk,omitempty\"`\n\t// dest_member_pk is the member who should receive the secret\n\tDestMemberPk []byte `protobuf:\"bytes,2,opt,name=dest_member_pk,json=destMemberPk,proto3\" json:\"dest_member_pk,omitempty\"`\n\t// payload is the serialization of Payload encrypted for the specified member\n\tPayload []byte `protobuf:\"bytes,3,opt,name=payload,proto3\" json:\"payload,omitempty\"`\n}\n\nfunc (x *GroupDeviceChainKeyAdded) Reset() {\n\t*x = GroupDeviceChainKeyAdded{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[14]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *GroupDeviceChainKeyAdded) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*GroupDeviceChainKeyAdded) ProtoMessage() {}\n\nfunc (x *GroupDeviceChainKeyAdded) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[14]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use GroupDeviceChainKeyAdded.ProtoReflect.Descriptor instead.\nfunc (*GroupDeviceChainKeyAdded) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{14}\n}\n\nfunc (x *GroupDeviceChainKeyAdded) GetDevicePk() []byte {\n\tif x != nil {\n\t\treturn x.DevicePk\n\t}\n\treturn nil\n}\n\nfunc (x *GroupDeviceChainKeyAdded) GetDestMemberPk() []byte {\n\tif x != nil {\n\t\treturn x.DestMemberPk\n\t}\n\treturn nil\n}\n\nfunc (x *GroupDeviceChainKeyAdded) GetPayload() []byte {\n\tif x != nil {\n\t\treturn x.Payload\n\t}\n\treturn nil\n}\n\n// MultiMemberGroupAliasResolverAdded indicates that a group member want to disclose their presence in the group to their contacts\ntype MultiMemberGroupAliasResolverAdded struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// device_pk is the device sending the event, signs the message\n\tDevicePk []byte `protobuf:\"bytes,1,opt,name=device_pk,json=devicePk,proto3\" json:\"device_pk,omitempty\"`\n\t// alias_resolver allows contact of an account to resolve the real identity behind an alias (Multi-Member Group Member)\n\t// Generated by both contacts and account independently using: hmac(aliasPK, GroupID)\n\tAliasResolver []byte `protobuf:\"bytes,2,opt,name=alias_resolver,json=aliasResolver,proto3\" json:\"alias_resolver,omitempty\"`\n\t// alias_proof ensures that the associated alias_resolver has been issued by the right account\n\t// Generated using aliasSKSig(GroupID)\n\tAliasProof []byte `protobuf:\"bytes,3,opt,name=alias_proof,json=aliasProof,proto3\" json:\"alias_proof,omitempty\"`\n}\n\nfunc (x *MultiMemberGroupAliasResolverAdded) Reset() {\n\t*x = MultiMemberGroupAliasResolverAdded{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[15]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *MultiMemberGroupAliasResolverAdded) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*MultiMemberGroupAliasResolverAdded) ProtoMessage() {}\n\nfunc (x *MultiMemberGroupAliasResolverAdded) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[15]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use MultiMemberGroupAliasResolverAdded.ProtoReflect.Descriptor instead.\nfunc (*MultiMemberGroupAliasResolverAdded) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{15}\n}\n\nfunc (x *MultiMemberGroupAliasResolverAdded) GetDevicePk() []byte {\n\tif x != nil {\n\t\treturn x.DevicePk\n\t}\n\treturn nil\n}\n\nfunc (x *MultiMemberGroupAliasResolverAdded) GetAliasResolver() []byte {\n\tif x != nil {\n\t\treturn x.AliasResolver\n\t}\n\treturn nil\n}\n\nfunc (x *MultiMemberGroupAliasResolverAdded) GetAliasProof() []byte {\n\tif x != nil {\n\t\treturn x.AliasProof\n\t}\n\treturn nil\n}\n\n// MultiMemberGroupAdminRoleGranted indicates that a group admin allows another group member to act as an admin\ntype MultiMemberGroupAdminRoleGranted struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// device_pk is the device sending the event, signs the message, must be the device of an admin of the group\n\tDevicePk []byte `protobuf:\"bytes,1,opt,name=device_pk,json=devicePk,proto3\" json:\"device_pk,omitempty\"`\n\t// grantee_member_pk is the member public key of the member granted of the admin role\n\tGranteeMemberPk []byte `protobuf:\"bytes,2,opt,name=grantee_member_pk,json=granteeMemberPk,proto3\" json:\"grantee_member_pk,omitempty\"`\n}\n\nfunc (x *MultiMemberGroupAdminRoleGranted) Reset() {\n\t*x = MultiMemberGroupAdminRoleGranted{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[16]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *MultiMemberGroupAdminRoleGranted) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*MultiMemberGroupAdminRoleGranted) ProtoMessage() {}\n\nfunc (x *MultiMemberGroupAdminRoleGranted) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[16]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use MultiMemberGroupAdminRoleGranted.ProtoReflect.Descriptor instead.\nfunc (*MultiMemberGroupAdminRoleGranted) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{16}\n}\n\nfunc (x *MultiMemberGroupAdminRoleGranted) GetDevicePk() []byte {\n\tif x != nil {\n\t\treturn x.DevicePk\n\t}\n\treturn nil\n}\n\nfunc (x *MultiMemberGroupAdminRoleGranted) GetGranteeMemberPk() []byte {\n\tif x != nil {\n\t\treturn x.GranteeMemberPk\n\t}\n\treturn nil\n}\n\n// MultiMemberGroupInitialMemberAnnounced indicates that a member is the group creator, this event is signed using the group ID private key\ntype MultiMemberGroupInitialMemberAnnounced struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// member_pk is the public key of the member who is the group creator\n\tMemberPk []byte `protobuf:\"bytes,1,opt,name=member_pk,json=memberPk,proto3\" json:\"member_pk,omitempty\"`\n}\n\nfunc (x *MultiMemberGroupInitialMemberAnnounced) Reset() {\n\t*x = MultiMemberGroupInitialMemberAnnounced{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[17]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *MultiMemberGroupInitialMemberAnnounced) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*MultiMemberGroupInitialMemberAnnounced) ProtoMessage() {}\n\nfunc (x *MultiMemberGroupInitialMemberAnnounced) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[17]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use MultiMemberGroupInitialMemberAnnounced.ProtoReflect.Descriptor instead.\nfunc (*MultiMemberGroupInitialMemberAnnounced) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{17}\n}\n\nfunc (x *MultiMemberGroupInitialMemberAnnounced) GetMemberPk() []byte {\n\tif x != nil {\n\t\treturn x.MemberPk\n\t}\n\treturn nil\n}\n\n// GroupAddAdditionalRendezvousSeed indicates that an additional rendezvous point should be used for data synchronization\ntype GroupAddAdditionalRendezvousSeed struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// device_pk is the device sending the event, signs the message, must be the device of an admin of the group\n\tDevicePk []byte `protobuf:\"bytes,1,opt,name=device_pk,json=devicePk,proto3\" json:\"device_pk,omitempty\"`\n\t// seed is the additional rendezvous point seed which should be used\n\tSeed []byte `protobuf:\"bytes,2,opt,name=seed,proto3\" json:\"seed,omitempty\"`\n}\n\nfunc (x *GroupAddAdditionalRendezvousSeed) Reset() {\n\t*x = GroupAddAdditionalRendezvousSeed{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[18]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *GroupAddAdditionalRendezvousSeed) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*GroupAddAdditionalRendezvousSeed) ProtoMessage() {}\n\nfunc (x *GroupAddAdditionalRendezvousSeed) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[18]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use GroupAddAdditionalRendezvousSeed.ProtoReflect.Descriptor instead.\nfunc (*GroupAddAdditionalRendezvousSeed) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{18}\n}\n\nfunc (x *GroupAddAdditionalRendezvousSeed) GetDevicePk() []byte {\n\tif x != nil {\n\t\treturn x.DevicePk\n\t}\n\treturn nil\n}\n\nfunc (x *GroupAddAdditionalRendezvousSeed) GetSeed() []byte {\n\tif x != nil {\n\t\treturn x.Seed\n\t}\n\treturn nil\n}\n\n// GroupRemoveAdditionalRendezvousSeed indicates that a previously added rendezvous point should be removed\ntype GroupRemoveAdditionalRendezvousSeed struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// device_pk is the device sending the event, signs the message, must be the device of an admin of the group\n\tDevicePk []byte `protobuf:\"bytes,1,opt,name=device_pk,json=devicePk,proto3\" json:\"device_pk,omitempty\"`\n\t// seed is the additional rendezvous point seed which should be removed\n\tSeed []byte `protobuf:\"bytes,2,opt,name=seed,proto3\" json:\"seed,omitempty\"`\n}\n\nfunc (x *GroupRemoveAdditionalRendezvousSeed) Reset() {\n\t*x = GroupRemoveAdditionalRendezvousSeed{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[19]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *GroupRemoveAdditionalRendezvousSeed) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*GroupRemoveAdditionalRendezvousSeed) ProtoMessage() {}\n\nfunc (x *GroupRemoveAdditionalRendezvousSeed) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[19]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use GroupRemoveAdditionalRendezvousSeed.ProtoReflect.Descriptor instead.\nfunc (*GroupRemoveAdditionalRendezvousSeed) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{19}\n}\n\nfunc (x *GroupRemoveAdditionalRendezvousSeed) GetDevicePk() []byte {\n\tif x != nil {\n\t\treturn x.DevicePk\n\t}\n\treturn nil\n}\n\nfunc (x *GroupRemoveAdditionalRendezvousSeed) GetSeed() []byte {\n\tif x != nil {\n\t\treturn x.Seed\n\t}\n\treturn nil\n}\n\n// AccountGroupJoined indicates that the account is now part of a new group\ntype AccountGroupJoined struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// device_pk is the device sending the event, signs the message\n\tDevicePk []byte `protobuf:\"bytes,1,opt,name=device_pk,json=devicePk,proto3\" json:\"device_pk,omitempty\"`\n\t// group describe the joined group\n\tGroup *Group `protobuf:\"bytes,2,opt,name=group,proto3\" json:\"group,omitempty\"`\n}\n\nfunc (x *AccountGroupJoined) Reset() {\n\t*x = AccountGroupJoined{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[20]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *AccountGroupJoined) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*AccountGroupJoined) ProtoMessage() {}\n\nfunc (x *AccountGroupJoined) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[20]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use AccountGroupJoined.ProtoReflect.Descriptor instead.\nfunc (*AccountGroupJoined) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{20}\n}\n\nfunc (x *AccountGroupJoined) GetDevicePk() []byte {\n\tif x != nil {\n\t\treturn x.DevicePk\n\t}\n\treturn nil\n}\n\nfunc (x *AccountGroupJoined) GetGroup() *Group {\n\tif x != nil {\n\t\treturn x.Group\n\t}\n\treturn nil\n}\n\n// AccountGroupLeft indicates that the account has left a group\ntype AccountGroupLeft struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// device_pk is the device sending the event, signs the message\n\tDevicePk []byte `protobuf:\"bytes,1,opt,name=device_pk,json=devicePk,proto3\" json:\"device_pk,omitempty\"`\n\t// group_pk references the group left\n\tGroupPk []byte `protobuf:\"bytes,2,opt,name=group_pk,json=groupPk,proto3\" json:\"group_pk,omitempty\"`\n}\n\nfunc (x *AccountGroupLeft) Reset() {\n\t*x = AccountGroupLeft{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[21]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *AccountGroupLeft) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*AccountGroupLeft) ProtoMessage() {}\n\nfunc (x *AccountGroupLeft) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[21]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use AccountGroupLeft.ProtoReflect.Descriptor instead.\nfunc (*AccountGroupLeft) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{21}\n}\n\nfunc (x *AccountGroupLeft) GetDevicePk() []byte {\n\tif x != nil {\n\t\treturn x.DevicePk\n\t}\n\treturn nil\n}\n\nfunc (x *AccountGroupLeft) GetGroupPk() []byte {\n\tif x != nil {\n\t\treturn x.GroupPk\n\t}\n\treturn nil\n}\n\n// AccountContactRequestDisabled indicates that the account should not be advertised on a public rendezvous point\ntype AccountContactRequestDisabled struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// device_pk is the device sending the event, signs the message\n\tDevicePk []byte `protobuf:\"bytes,1,opt,name=device_pk,json=devicePk,proto3\" json:\"device_pk,omitempty\"`\n}\n\nfunc (x *AccountContactRequestDisabled) Reset() {\n\t*x = AccountContactRequestDisabled{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[22]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *AccountContactRequestDisabled) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*AccountContactRequestDisabled) ProtoMessage() {}\n\nfunc (x *AccountContactRequestDisabled) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[22]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use AccountContactRequestDisabled.ProtoReflect.Descriptor instead.\nfunc (*AccountContactRequestDisabled) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{22}\n}\n\nfunc (x *AccountContactRequestDisabled) GetDevicePk() []byte {\n\tif x != nil {\n\t\treturn x.DevicePk\n\t}\n\treturn nil\n}\n\n// AccountContactRequestEnabled indicates that the account should be advertised on a public rendezvous point\ntype AccountContactRequestEnabled struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// device_pk is the device sending the event, signs the message\n\tDevicePk []byte `protobuf:\"bytes,1,opt,name=device_pk,json=devicePk,proto3\" json:\"device_pk,omitempty\"`\n}\n\nfunc (x *AccountContactRequestEnabled) Reset() {\n\t*x = AccountContactRequestEnabled{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[23]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *AccountContactRequestEnabled) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*AccountContactRequestEnabled) ProtoMessage() {}\n\nfunc (x *AccountContactRequestEnabled) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[23]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use AccountContactRequestEnabled.ProtoReflect.Descriptor instead.\nfunc (*AccountContactRequestEnabled) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{23}\n}\n\nfunc (x *AccountContactRequestEnabled) GetDevicePk() []byte {\n\tif x != nil {\n\t\treturn x.DevicePk\n\t}\n\treturn nil\n}\n\n// AccountContactRequestReferenceReset indicates that the account should be advertised on different public rendezvous points\ntype AccountContactRequestReferenceReset struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// device_pk is the device sending the event, signs the message\n\tDevicePk []byte `protobuf:\"bytes,1,opt,name=device_pk,json=devicePk,proto3\" json:\"device_pk,omitempty\"`\n\t// public_rendezvous_seed is the new rendezvous point seed\n\tPublicRendezvousSeed []byte `protobuf:\"bytes,2,opt,name=public_rendezvous_seed,json=publicRendezvousSeed,proto3\" json:\"public_rendezvous_seed,omitempty\"`\n}\n\nfunc (x *AccountContactRequestReferenceReset) Reset() {\n\t*x = AccountContactRequestReferenceReset{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[24]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *AccountContactRequestReferenceReset) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*AccountContactRequestReferenceReset) ProtoMessage() {}\n\nfunc (x *AccountContactRequestReferenceReset) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[24]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use AccountContactRequestReferenceReset.ProtoReflect.Descriptor instead.\nfunc (*AccountContactRequestReferenceReset) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{24}\n}\n\nfunc (x *AccountContactRequestReferenceReset) GetDevicePk() []byte {\n\tif x != nil {\n\t\treturn x.DevicePk\n\t}\n\treturn nil\n}\n\nfunc (x *AccountContactRequestReferenceReset) GetPublicRendezvousSeed() []byte {\n\tif x != nil {\n\t\treturn x.PublicRendezvousSeed\n\t}\n\treturn nil\n}\n\n// This event should be followed by an AccountGroupJoined event\n// This event should be followed by a GroupMemberDeviceAdded event within the AccountGroup\n// This event should be followed by a GroupDeviceChainKeyAdded event within the AccountGroup\n// AccountContactRequestOutgoingEnqueued indicates that the account will attempt to send a contact request when a matching peer is discovered\ntype AccountContactRequestOutgoingEnqueued struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// device_pk is the device sending the event, signs the message\n\tDevicePk []byte `protobuf:\"bytes,1,opt,name=device_pk,json=devicePk,proto3\" json:\"device_pk,omitempty\"`\n\t// group_pk is the 1to1 group with the requested user\n\tGroupPk []byte `protobuf:\"bytes,2,opt,name=group_pk,json=groupPk,proto3\" json:\"group_pk,omitempty\"`\n\t// contact is a message describing how to connect to the other account\n\tContact *ShareableContact `protobuf:\"bytes,3,opt,name=contact,proto3\" json:\"contact,omitempty\"`\n\t// own_metadata is the identifying metadata that will be shared to the other account\n\tOwnMetadata []byte `protobuf:\"bytes,4,opt,name=own_metadata,json=ownMetadata,proto3\" json:\"own_metadata,omitempty\"`\n}\n\nfunc (x *AccountContactRequestOutgoingEnqueued) Reset() {\n\t*x = AccountContactRequestOutgoingEnqueued{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[25]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *AccountContactRequestOutgoingEnqueued) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*AccountContactRequestOutgoingEnqueued) ProtoMessage() {}\n\nfunc (x *AccountContactRequestOutgoingEnqueued) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[25]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use AccountContactRequestOutgoingEnqueued.ProtoReflect.Descriptor instead.\nfunc (*AccountContactRequestOutgoingEnqueued) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{25}\n}\n\nfunc (x *AccountContactRequestOutgoingEnqueued) GetDevicePk() []byte {\n\tif x != nil {\n\t\treturn x.DevicePk\n\t}\n\treturn nil\n}\n\nfunc (x *AccountContactRequestOutgoingEnqueued) GetGroupPk() []byte {\n\tif x != nil {\n\t\treturn x.GroupPk\n\t}\n\treturn nil\n}\n\nfunc (x *AccountContactRequestOutgoingEnqueued) GetContact() *ShareableContact {\n\tif x != nil {\n\t\treturn x.Contact\n\t}\n\treturn nil\n}\n\nfunc (x *AccountContactRequestOutgoingEnqueued) GetOwnMetadata() []byte {\n\tif x != nil {\n\t\treturn x.OwnMetadata\n\t}\n\treturn nil\n}\n\n// AccountContactRequestOutgoingSent indicates that the account has sent a contact request\ntype AccountContactRequestOutgoingSent struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// device_pk is the device sending the account event, signs the message\n\tDevicePk []byte `protobuf:\"bytes,1,opt,name=device_pk,json=devicePk,proto3\" json:\"device_pk,omitempty\"`\n\t// contact_pk is the contacted account\n\tContactPk []byte `protobuf:\"bytes,2,opt,name=contact_pk,json=contactPk,proto3\" json:\"contact_pk,omitempty\"`\n}\n\nfunc (x *AccountContactRequestOutgoingSent) Reset() {\n\t*x = AccountContactRequestOutgoingSent{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[26]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *AccountContactRequestOutgoingSent) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*AccountContactRequestOutgoingSent) ProtoMessage() {}\n\nfunc (x *AccountContactRequestOutgoingSent) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[26]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use AccountContactRequestOutgoingSent.ProtoReflect.Descriptor instead.\nfunc (*AccountContactRequestOutgoingSent) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{26}\n}\n\nfunc (x *AccountContactRequestOutgoingSent) GetDevicePk() []byte {\n\tif x != nil {\n\t\treturn x.DevicePk\n\t}\n\treturn nil\n}\n\nfunc (x *AccountContactRequestOutgoingSent) GetContactPk() []byte {\n\tif x != nil {\n\t\treturn x.ContactPk\n\t}\n\treturn nil\n}\n\n// AccountContactRequestIncomingReceived indicates that the account has received a new contact request\ntype AccountContactRequestIncomingReceived struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// device_pk is the device sending the account event (which received the contact request), signs the message\n\tDevicePk []byte `protobuf:\"bytes,1,opt,name=device_pk,json=devicePk,proto3\" json:\"device_pk,omitempty\"`\n\t// contact_pk is the account sending the request\n\tContactPk []byte `protobuf:\"bytes,2,opt,name=contact_pk,json=contactPk,proto3\" json:\"contact_pk,omitempty\"`\n\t// TODO: is this necessary?\n\t// contact_rendezvous_seed is the rendezvous seed of the contact sending the request\n\tContactRendezvousSeed []byte `protobuf:\"bytes,3,opt,name=contact_rendezvous_seed,json=contactRendezvousSeed,proto3\" json:\"contact_rendezvous_seed,omitempty\"`\n\t// TODO: is this necessary?\n\t// contact_metadata is the metadata specific to the app to identify the contact for the request\n\tContactMetadata []byte `protobuf:\"bytes,4,opt,name=contact_metadata,json=contactMetadata,proto3\" json:\"contact_metadata,omitempty\"`\n}\n\nfunc (x *AccountContactRequestIncomingReceived) Reset() {\n\t*x = AccountContactRequestIncomingReceived{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[27]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *AccountContactRequestIncomingReceived) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*AccountContactRequestIncomingReceived) ProtoMessage() {}\n\nfunc (x *AccountContactRequestIncomingReceived) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[27]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use AccountContactRequestIncomingReceived.ProtoReflect.Descriptor instead.\nfunc (*AccountContactRequestIncomingReceived) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{27}\n}\n\nfunc (x *AccountContactRequestIncomingReceived) GetDevicePk() []byte {\n\tif x != nil {\n\t\treturn x.DevicePk\n\t}\n\treturn nil\n}\n\nfunc (x *AccountContactRequestIncomingReceived) GetContactPk() []byte {\n\tif x != nil {\n\t\treturn x.ContactPk\n\t}\n\treturn nil\n}\n\nfunc (x *AccountContactRequestIncomingReceived) GetContactRendezvousSeed() []byte {\n\tif x != nil {\n\t\treturn x.ContactRendezvousSeed\n\t}\n\treturn nil\n}\n\nfunc (x *AccountContactRequestIncomingReceived) GetContactMetadata() []byte {\n\tif x != nil {\n\t\treturn x.ContactMetadata\n\t}\n\treturn nil\n}\n\n// AccountContactRequestIncomingDiscarded indicates that a contact request has been refused\ntype AccountContactRequestIncomingDiscarded struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// device_pk is the device sending the event, signs the message\n\tDevicePk []byte `protobuf:\"bytes,1,opt,name=device_pk,json=devicePk,proto3\" json:\"device_pk,omitempty\"`\n\t// contact_pk is the contact whom request is refused\n\tContactPk []byte `protobuf:\"bytes,2,opt,name=contact_pk,json=contactPk,proto3\" json:\"contact_pk,omitempty\"`\n}\n\nfunc (x *AccountContactRequestIncomingDiscarded) Reset() {\n\t*x = AccountContactRequestIncomingDiscarded{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[28]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *AccountContactRequestIncomingDiscarded) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*AccountContactRequestIncomingDiscarded) ProtoMessage() {}\n\nfunc (x *AccountContactRequestIncomingDiscarded) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[28]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use AccountContactRequestIncomingDiscarded.ProtoReflect.Descriptor instead.\nfunc (*AccountContactRequestIncomingDiscarded) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{28}\n}\n\nfunc (x *AccountContactRequestIncomingDiscarded) GetDevicePk() []byte {\n\tif x != nil {\n\t\treturn x.DevicePk\n\t}\n\treturn nil\n}\n\nfunc (x *AccountContactRequestIncomingDiscarded) GetContactPk() []byte {\n\tif x != nil {\n\t\treturn x.ContactPk\n\t}\n\treturn nil\n}\n\n// This event should be followed by an AccountGroupJoined event\n// This event should be followed by GroupMemberDeviceAdded and GroupDeviceChainKeyAdded events within the AccountGroup\n// AccountContactRequestIncomingAccepted indicates that a contact request has been accepted\ntype AccountContactRequestIncomingAccepted struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// device_pk is the device sending the event, signs the message\n\tDevicePk []byte `protobuf:\"bytes,1,opt,name=device_pk,json=devicePk,proto3\" json:\"device_pk,omitempty\"`\n\t// contact_pk is the contact whom request is accepted\n\tContactPk []byte `protobuf:\"bytes,2,opt,name=contact_pk,json=contactPk,proto3\" json:\"contact_pk,omitempty\"`\n\t// group_pk is the 1to1 group with the requester user\n\tGroupPk []byte `protobuf:\"bytes,3,opt,name=group_pk,json=groupPk,proto3\" json:\"group_pk,omitempty\"`\n}\n\nfunc (x *AccountContactRequestIncomingAccepted) Reset() {\n\t*x = AccountContactRequestIncomingAccepted{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[29]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *AccountContactRequestIncomingAccepted) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*AccountContactRequestIncomingAccepted) ProtoMessage() {}\n\nfunc (x *AccountContactRequestIncomingAccepted) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[29]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use AccountContactRequestIncomingAccepted.ProtoReflect.Descriptor instead.\nfunc (*AccountContactRequestIncomingAccepted) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{29}\n}\n\nfunc (x *AccountContactRequestIncomingAccepted) GetDevicePk() []byte {\n\tif x != nil {\n\t\treturn x.DevicePk\n\t}\n\treturn nil\n}\n\nfunc (x *AccountContactRequestIncomingAccepted) GetContactPk() []byte {\n\tif x != nil {\n\t\treturn x.ContactPk\n\t}\n\treturn nil\n}\n\nfunc (x *AccountContactRequestIncomingAccepted) GetGroupPk() []byte {\n\tif x != nil {\n\t\treturn x.GroupPk\n\t}\n\treturn nil\n}\n\n// AccountContactBlocked indicates that a contact is blocked\ntype AccountContactBlocked struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// device_pk is the device sending the event, signs the message\n\tDevicePk []byte `protobuf:\"bytes,1,opt,name=device_pk,json=devicePk,proto3\" json:\"device_pk,omitempty\"`\n\t// contact_pk is the contact blocked\n\tContactPk []byte `protobuf:\"bytes,2,opt,name=contact_pk,json=contactPk,proto3\" json:\"contact_pk,omitempty\"`\n}\n\nfunc (x *AccountContactBlocked) Reset() {\n\t*x = AccountContactBlocked{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[30]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *AccountContactBlocked) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*AccountContactBlocked) ProtoMessage() {}\n\nfunc (x *AccountContactBlocked) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[30]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use AccountContactBlocked.ProtoReflect.Descriptor instead.\nfunc (*AccountContactBlocked) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{30}\n}\n\nfunc (x *AccountContactBlocked) GetDevicePk() []byte {\n\tif x != nil {\n\t\treturn x.DevicePk\n\t}\n\treturn nil\n}\n\nfunc (x *AccountContactBlocked) GetContactPk() []byte {\n\tif x != nil {\n\t\treturn x.ContactPk\n\t}\n\treturn nil\n}\n\n// AccountContactUnblocked indicates that a contact is unblocked\ntype AccountContactUnblocked struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// device_pk is the device sending the event, signs the message\n\tDevicePk []byte `protobuf:\"bytes,1,opt,name=device_pk,json=devicePk,proto3\" json:\"device_pk,omitempty\"`\n\t// contact_pk is the contact unblocked\n\tContactPk []byte `protobuf:\"bytes,2,opt,name=contact_pk,json=contactPk,proto3\" json:\"contact_pk,omitempty\"`\n}\n\nfunc (x *AccountContactUnblocked) Reset() {\n\t*x = AccountContactUnblocked{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[31]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *AccountContactUnblocked) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*AccountContactUnblocked) ProtoMessage() {}\n\nfunc (x *AccountContactUnblocked) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[31]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use AccountContactUnblocked.ProtoReflect.Descriptor instead.\nfunc (*AccountContactUnblocked) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{31}\n}\n\nfunc (x *AccountContactUnblocked) GetDevicePk() []byte {\n\tif x != nil {\n\t\treturn x.DevicePk\n\t}\n\treturn nil\n}\n\nfunc (x *AccountContactUnblocked) GetContactPk() []byte {\n\tif x != nil {\n\t\treturn x.ContactPk\n\t}\n\treturn nil\n}\n\ntype GroupReplicating struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// device_pk is the device sending the event, signs the message\n\tDevicePk []byte `protobuf:\"bytes,1,opt,name=device_pk,json=devicePk,proto3\" json:\"device_pk,omitempty\"`\n\t// authentication_url indicates which server has been used for authentication\n\tAuthenticationUrl string `protobuf:\"bytes,2,opt,name=authentication_url,json=authenticationUrl,proto3\" json:\"authentication_url,omitempty\"`\n\t// replication_server indicates which server will be used for replication\n\tReplicationServer string `protobuf:\"bytes,3,opt,name=replication_server,json=replicationServer,proto3\" json:\"replication_server,omitempty\"`\n}\n\nfunc (x *GroupReplicating) Reset() {\n\t*x = GroupReplicating{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[32]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *GroupReplicating) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*GroupReplicating) ProtoMessage() {}\n\nfunc (x *GroupReplicating) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[32]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use GroupReplicating.ProtoReflect.Descriptor instead.\nfunc (*GroupReplicating) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{32}\n}\n\nfunc (x *GroupReplicating) GetDevicePk() []byte {\n\tif x != nil {\n\t\treturn x.DevicePk\n\t}\n\treturn nil\n}\n\nfunc (x *GroupReplicating) GetAuthenticationUrl() string {\n\tif x != nil {\n\t\treturn x.AuthenticationUrl\n\t}\n\treturn \"\"\n}\n\nfunc (x *GroupReplicating) GetReplicationServer() string {\n\tif x != nil {\n\t\treturn x.ReplicationServer\n\t}\n\treturn \"\"\n}\n\ntype ServiceExportData struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *ServiceExportData) Reset() {\n\t*x = ServiceExportData{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[33]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ServiceExportData) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ServiceExportData) ProtoMessage() {}\n\nfunc (x *ServiceExportData) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[33]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ServiceExportData.ProtoReflect.Descriptor instead.\nfunc (*ServiceExportData) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{33}\n}\n\ntype ServiceGetConfiguration struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *ServiceGetConfiguration) Reset() {\n\t*x = ServiceGetConfiguration{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[34]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ServiceGetConfiguration) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ServiceGetConfiguration) ProtoMessage() {}\n\nfunc (x *ServiceGetConfiguration) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[34]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ServiceGetConfiguration.ProtoReflect.Descriptor instead.\nfunc (*ServiceGetConfiguration) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{34}\n}\n\ntype ContactRequestReference struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *ContactRequestReference) Reset() {\n\t*x = ContactRequestReference{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[35]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ContactRequestReference) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ContactRequestReference) ProtoMessage() {}\n\nfunc (x *ContactRequestReference) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[35]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ContactRequestReference.ProtoReflect.Descriptor instead.\nfunc (*ContactRequestReference) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{35}\n}\n\ntype ContactRequestDisable struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *ContactRequestDisable) Reset() {\n\t*x = ContactRequestDisable{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[36]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ContactRequestDisable) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ContactRequestDisable) ProtoMessage() {}\n\nfunc (x *ContactRequestDisable) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[36]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ContactRequestDisable.ProtoReflect.Descriptor instead.\nfunc (*ContactRequestDisable) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{36}\n}\n\ntype ContactRequestEnable struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *ContactRequestEnable) Reset() {\n\t*x = ContactRequestEnable{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[37]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ContactRequestEnable) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ContactRequestEnable) ProtoMessage() {}\n\nfunc (x *ContactRequestEnable) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[37]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ContactRequestEnable.ProtoReflect.Descriptor instead.\nfunc (*ContactRequestEnable) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{37}\n}\n\ntype ContactRequestResetReference struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *ContactRequestResetReference) Reset() {\n\t*x = ContactRequestResetReference{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[38]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ContactRequestResetReference) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ContactRequestResetReference) ProtoMessage() {}\n\nfunc (x *ContactRequestResetReference) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[38]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ContactRequestResetReference.ProtoReflect.Descriptor instead.\nfunc (*ContactRequestResetReference) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{38}\n}\n\ntype ContactRequestSend struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *ContactRequestSend) Reset() {\n\t*x = ContactRequestSend{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[39]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ContactRequestSend) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ContactRequestSend) ProtoMessage() {}\n\nfunc (x *ContactRequestSend) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[39]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ContactRequestSend.ProtoReflect.Descriptor instead.\nfunc (*ContactRequestSend) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{39}\n}\n\ntype ContactRequestAccept struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *ContactRequestAccept) Reset() {\n\t*x = ContactRequestAccept{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[40]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ContactRequestAccept) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ContactRequestAccept) ProtoMessage() {}\n\nfunc (x *ContactRequestAccept) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[40]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ContactRequestAccept.ProtoReflect.Descriptor instead.\nfunc (*ContactRequestAccept) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{40}\n}\n\ntype ContactRequestDiscard struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *ContactRequestDiscard) Reset() {\n\t*x = ContactRequestDiscard{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[41]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ContactRequestDiscard) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ContactRequestDiscard) ProtoMessage() {}\n\nfunc (x *ContactRequestDiscard) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[41]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ContactRequestDiscard.ProtoReflect.Descriptor instead.\nfunc (*ContactRequestDiscard) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{41}\n}\n\ntype ShareContact struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *ShareContact) Reset() {\n\t*x = ShareContact{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[42]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ShareContact) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ShareContact) ProtoMessage() {}\n\nfunc (x *ShareContact) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[42]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ShareContact.ProtoReflect.Descriptor instead.\nfunc (*ShareContact) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{42}\n}\n\ntype DecodeContact struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *DecodeContact) Reset() {\n\t*x = DecodeContact{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[43]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *DecodeContact) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*DecodeContact) ProtoMessage() {}\n\nfunc (x *DecodeContact) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[43]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use DecodeContact.ProtoReflect.Descriptor instead.\nfunc (*DecodeContact) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{43}\n}\n\ntype ContactBlock struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *ContactBlock) Reset() {\n\t*x = ContactBlock{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[44]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ContactBlock) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ContactBlock) ProtoMessage() {}\n\nfunc (x *ContactBlock) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[44]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ContactBlock.ProtoReflect.Descriptor instead.\nfunc (*ContactBlock) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{44}\n}\n\ntype ContactUnblock struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *ContactUnblock) Reset() {\n\t*x = ContactUnblock{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[45]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ContactUnblock) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ContactUnblock) ProtoMessage() {}\n\nfunc (x *ContactUnblock) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[45]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ContactUnblock.ProtoReflect.Descriptor instead.\nfunc (*ContactUnblock) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{45}\n}\n\ntype ContactAliasKeySend struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *ContactAliasKeySend) Reset() {\n\t*x = ContactAliasKeySend{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[46]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ContactAliasKeySend) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ContactAliasKeySend) ProtoMessage() {}\n\nfunc (x *ContactAliasKeySend) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[46]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ContactAliasKeySend.ProtoReflect.Descriptor instead.\nfunc (*ContactAliasKeySend) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{46}\n}\n\ntype MultiMemberGroupCreate struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *MultiMemberGroupCreate) Reset() {\n\t*x = MultiMemberGroupCreate{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[47]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *MultiMemberGroupCreate) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*MultiMemberGroupCreate) ProtoMessage() {}\n\nfunc (x *MultiMemberGroupCreate) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[47]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use MultiMemberGroupCreate.ProtoReflect.Descriptor instead.\nfunc (*MultiMemberGroupCreate) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{47}\n}\n\ntype MultiMemberGroupJoin struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *MultiMemberGroupJoin) Reset() {\n\t*x = MultiMemberGroupJoin{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[48]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *MultiMemberGroupJoin) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*MultiMemberGroupJoin) ProtoMessage() {}\n\nfunc (x *MultiMemberGroupJoin) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[48]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use MultiMemberGroupJoin.ProtoReflect.Descriptor instead.\nfunc (*MultiMemberGroupJoin) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{48}\n}\n\ntype MultiMemberGroupLeave struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *MultiMemberGroupLeave) Reset() {\n\t*x = MultiMemberGroupLeave{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[49]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *MultiMemberGroupLeave) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*MultiMemberGroupLeave) ProtoMessage() {}\n\nfunc (x *MultiMemberGroupLeave) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[49]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use MultiMemberGroupLeave.ProtoReflect.Descriptor instead.\nfunc (*MultiMemberGroupLeave) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{49}\n}\n\ntype MultiMemberGroupAliasResolverDisclose struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *MultiMemberGroupAliasResolverDisclose) Reset() {\n\t*x = MultiMemberGroupAliasResolverDisclose{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[50]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *MultiMemberGroupAliasResolverDisclose) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*MultiMemberGroupAliasResolverDisclose) ProtoMessage() {}\n\nfunc (x *MultiMemberGroupAliasResolverDisclose) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[50]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use MultiMemberGroupAliasResolverDisclose.ProtoReflect.Descriptor instead.\nfunc (*MultiMemberGroupAliasResolverDisclose) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{50}\n}\n\ntype MultiMemberGroupAdminRoleGrant struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *MultiMemberGroupAdminRoleGrant) Reset() {\n\t*x = MultiMemberGroupAdminRoleGrant{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[51]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *MultiMemberGroupAdminRoleGrant) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*MultiMemberGroupAdminRoleGrant) ProtoMessage() {}\n\nfunc (x *MultiMemberGroupAdminRoleGrant) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[51]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use MultiMemberGroupAdminRoleGrant.ProtoReflect.Descriptor instead.\nfunc (*MultiMemberGroupAdminRoleGrant) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{51}\n}\n\ntype MultiMemberGroupInvitationCreate struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *MultiMemberGroupInvitationCreate) Reset() {\n\t*x = MultiMemberGroupInvitationCreate{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[52]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *MultiMemberGroupInvitationCreate) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*MultiMemberGroupInvitationCreate) ProtoMessage() {}\n\nfunc (x *MultiMemberGroupInvitationCreate) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[52]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use MultiMemberGroupInvitationCreate.ProtoReflect.Descriptor instead.\nfunc (*MultiMemberGroupInvitationCreate) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{52}\n}\n\ntype AppMetadataSend struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *AppMetadataSend) Reset() {\n\t*x = AppMetadataSend{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[53]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *AppMetadataSend) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*AppMetadataSend) ProtoMessage() {}\n\nfunc (x *AppMetadataSend) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[53]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use AppMetadataSend.ProtoReflect.Descriptor instead.\nfunc (*AppMetadataSend) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{53}\n}\n\ntype AppMessageSend struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *AppMessageSend) Reset() {\n\t*x = AppMessageSend{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[54]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *AppMessageSend) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*AppMessageSend) ProtoMessage() {}\n\nfunc (x *AppMessageSend) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[54]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use AppMessageSend.ProtoReflect.Descriptor instead.\nfunc (*AppMessageSend) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{54}\n}\n\ntype GroupMetadataEvent struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// event_context contains context information about the event\n\tEventContext *EventContext `protobuf:\"bytes,1,opt,name=event_context,json=eventContext,proto3\" json:\"event_context,omitempty\"`\n\t// metadata contains the newly available metadata\n\tMetadata *GroupMetadata `protobuf:\"bytes,2,opt,name=metadata,proto3\" json:\"metadata,omitempty\"`\n\t// event_clear clear bytes for the event\n\tEvent []byte `protobuf:\"bytes,3,opt,name=event,proto3\" json:\"event,omitempty\"`\n}\n\nfunc (x *GroupMetadataEvent) Reset() {\n\t*x = GroupMetadataEvent{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[55]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *GroupMetadataEvent) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*GroupMetadataEvent) ProtoMessage() {}\n\nfunc (x *GroupMetadataEvent) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[55]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use GroupMetadataEvent.ProtoReflect.Descriptor instead.\nfunc (*GroupMetadataEvent) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{55}\n}\n\nfunc (x *GroupMetadataEvent) GetEventContext() *EventContext {\n\tif x != nil {\n\t\treturn x.EventContext\n\t}\n\treturn nil\n}\n\nfunc (x *GroupMetadataEvent) GetMetadata() *GroupMetadata {\n\tif x != nil {\n\t\treturn x.Metadata\n\t}\n\treturn nil\n}\n\nfunc (x *GroupMetadataEvent) GetEvent() []byte {\n\tif x != nil {\n\t\treturn x.Event\n\t}\n\treturn nil\n}\n\ntype GroupMessageEvent struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// event_context contains context information about the event\n\tEventContext *EventContext `protobuf:\"bytes,1,opt,name=event_context,json=eventContext,proto3\" json:\"event_context,omitempty\"`\n\t// headers contains headers of the secure message\n\tHeaders *MessageHeaders `protobuf:\"bytes,2,opt,name=headers,proto3\" json:\"headers,omitempty\"`\n\t// message contains the secure message payload\n\tMessage []byte `protobuf:\"bytes,3,opt,name=message,proto3\" json:\"message,omitempty\"`\n}\n\nfunc (x *GroupMessageEvent) Reset() {\n\t*x = GroupMessageEvent{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[56]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *GroupMessageEvent) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*GroupMessageEvent) ProtoMessage() {}\n\nfunc (x *GroupMessageEvent) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[56]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use GroupMessageEvent.ProtoReflect.Descriptor instead.\nfunc (*GroupMessageEvent) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{56}\n}\n\nfunc (x *GroupMessageEvent) GetEventContext() *EventContext {\n\tif x != nil {\n\t\treturn x.EventContext\n\t}\n\treturn nil\n}\n\nfunc (x *GroupMessageEvent) GetHeaders() *MessageHeaders {\n\tif x != nil {\n\t\treturn x.Headers\n\t}\n\treturn nil\n}\n\nfunc (x *GroupMessageEvent) GetMessage() []byte {\n\tif x != nil {\n\t\treturn x.Message\n\t}\n\treturn nil\n}\n\ntype GroupMetadataList struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *GroupMetadataList) Reset() {\n\t*x = GroupMetadataList{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[57]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *GroupMetadataList) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*GroupMetadataList) ProtoMessage() {}\n\nfunc (x *GroupMetadataList) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[57]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use GroupMetadataList.ProtoReflect.Descriptor instead.\nfunc (*GroupMetadataList) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{57}\n}\n\ntype GroupMessageList struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *GroupMessageList) Reset() {\n\t*x = GroupMessageList{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[58]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *GroupMessageList) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*GroupMessageList) ProtoMessage() {}\n\nfunc (x *GroupMessageList) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[58]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use GroupMessageList.ProtoReflect.Descriptor instead.\nfunc (*GroupMessageList) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{58}\n}\n\ntype GroupInfo struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *GroupInfo) Reset() {\n\t*x = GroupInfo{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[59]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *GroupInfo) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*GroupInfo) ProtoMessage() {}\n\nfunc (x *GroupInfo) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[59]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use GroupInfo.ProtoReflect.Descriptor instead.\nfunc (*GroupInfo) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{59}\n}\n\ntype ActivateGroup struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *ActivateGroup) Reset() {\n\t*x = ActivateGroup{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[60]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ActivateGroup) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ActivateGroup) ProtoMessage() {}\n\nfunc (x *ActivateGroup) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[60]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ActivateGroup.ProtoReflect.Descriptor instead.\nfunc (*ActivateGroup) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{60}\n}\n\ntype DeactivateGroup struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *DeactivateGroup) Reset() {\n\t*x = DeactivateGroup{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[61]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *DeactivateGroup) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*DeactivateGroup) ProtoMessage() {}\n\nfunc (x *DeactivateGroup) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[61]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use DeactivateGroup.ProtoReflect.Descriptor instead.\nfunc (*DeactivateGroup) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{61}\n}\n\ntype GroupDeviceStatus struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *GroupDeviceStatus) Reset() {\n\t*x = GroupDeviceStatus{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[62]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *GroupDeviceStatus) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*GroupDeviceStatus) ProtoMessage() {}\n\nfunc (x *GroupDeviceStatus) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[62]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use GroupDeviceStatus.ProtoReflect.Descriptor instead.\nfunc (*GroupDeviceStatus) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{62}\n}\n\ntype DebugListGroups struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *DebugListGroups) Reset() {\n\t*x = DebugListGroups{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[63]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *DebugListGroups) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*DebugListGroups) ProtoMessage() {}\n\nfunc (x *DebugListGroups) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[63]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use DebugListGroups.ProtoReflect.Descriptor instead.\nfunc (*DebugListGroups) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{63}\n}\n\ntype DebugInspectGroupStore struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *DebugInspectGroupStore) Reset() {\n\t*x = DebugInspectGroupStore{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[64]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *DebugInspectGroupStore) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*DebugInspectGroupStore) ProtoMessage() {}\n\nfunc (x *DebugInspectGroupStore) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[64]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use DebugInspectGroupStore.ProtoReflect.Descriptor instead.\nfunc (*DebugInspectGroupStore) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{64}\n}\n\ntype DebugGroup struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *DebugGroup) Reset() {\n\t*x = DebugGroup{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[65]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *DebugGroup) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*DebugGroup) ProtoMessage() {}\n\nfunc (x *DebugGroup) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[65]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use DebugGroup.ProtoReflect.Descriptor instead.\nfunc (*DebugGroup) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{65}\n}\n\ntype ShareableContact struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// pk is the account to send a contact request to\n\tPk []byte `protobuf:\"bytes,1,opt,name=pk,proto3\" json:\"pk,omitempty\"`\n\t// public_rendezvous_seed is the rendezvous seed used by the account to send a contact request to\n\tPublicRendezvousSeed []byte `protobuf:\"bytes,2,opt,name=public_rendezvous_seed,json=publicRendezvousSeed,proto3\" json:\"public_rendezvous_seed,omitempty\"`\n\t// metadata is the metadata specific to the app to identify the contact for the request\n\tMetadata []byte `protobuf:\"bytes,3,opt,name=metadata,proto3\" json:\"metadata,omitempty\"`\n}\n\nfunc (x *ShareableContact) Reset() {\n\t*x = ShareableContact{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[66]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ShareableContact) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ShareableContact) ProtoMessage() {}\n\nfunc (x *ShareableContact) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[66]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ShareableContact.ProtoReflect.Descriptor instead.\nfunc (*ShareableContact) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{66}\n}\n\nfunc (x *ShareableContact) GetPk() []byte {\n\tif x != nil {\n\t\treturn x.Pk\n\t}\n\treturn nil\n}\n\nfunc (x *ShareableContact) GetPublicRendezvousSeed() []byte {\n\tif x != nil {\n\t\treturn x.PublicRendezvousSeed\n\t}\n\treturn nil\n}\n\nfunc (x *ShareableContact) GetMetadata() []byte {\n\tif x != nil {\n\t\treturn x.Metadata\n\t}\n\treturn nil\n}\n\ntype ServiceTokenSupportedService struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tServiceType     string `protobuf:\"bytes,1,opt,name=service_type,json=serviceType,proto3\" json:\"service_type,omitempty\"`\n\tServiceEndpoint string `protobuf:\"bytes,2,opt,name=service_endpoint,json=serviceEndpoint,proto3\" json:\"service_endpoint,omitempty\"`\n}\n\nfunc (x *ServiceTokenSupportedService) Reset() {\n\t*x = ServiceTokenSupportedService{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[67]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ServiceTokenSupportedService) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ServiceTokenSupportedService) ProtoMessage() {}\n\nfunc (x *ServiceTokenSupportedService) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[67]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ServiceTokenSupportedService.ProtoReflect.Descriptor instead.\nfunc (*ServiceTokenSupportedService) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{67}\n}\n\nfunc (x *ServiceTokenSupportedService) GetServiceType() string {\n\tif x != nil {\n\t\treturn x.ServiceType\n\t}\n\treturn \"\"\n}\n\nfunc (x *ServiceTokenSupportedService) GetServiceEndpoint() string {\n\tif x != nil {\n\t\treturn x.ServiceEndpoint\n\t}\n\treturn \"\"\n}\n\ntype ServiceToken struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tToken             string                          `protobuf:\"bytes,1,opt,name=token,proto3\" json:\"token,omitempty\"`\n\tAuthenticationUrl string                          `protobuf:\"bytes,2,opt,name=authentication_url,json=authenticationUrl,proto3\" json:\"authentication_url,omitempty\"`\n\tSupportedServices []*ServiceTokenSupportedService `protobuf:\"bytes,3,rep,name=supported_services,json=supportedServices,proto3\" json:\"supported_services,omitempty\"`\n\tExpiration        int64                           `protobuf:\"varint,4,opt,name=expiration,proto3\" json:\"expiration,omitempty\"`\n}\n\nfunc (x *ServiceToken) Reset() {\n\t*x = ServiceToken{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[68]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ServiceToken) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ServiceToken) ProtoMessage() {}\n\nfunc (x *ServiceToken) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[68]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ServiceToken.ProtoReflect.Descriptor instead.\nfunc (*ServiceToken) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{68}\n}\n\nfunc (x *ServiceToken) GetToken() string {\n\tif x != nil {\n\t\treturn x.Token\n\t}\n\treturn \"\"\n}\n\nfunc (x *ServiceToken) GetAuthenticationUrl() string {\n\tif x != nil {\n\t\treturn x.AuthenticationUrl\n\t}\n\treturn \"\"\n}\n\nfunc (x *ServiceToken) GetSupportedServices() []*ServiceTokenSupportedService {\n\tif x != nil {\n\t\treturn x.SupportedServices\n\t}\n\treturn nil\n}\n\nfunc (x *ServiceToken) GetExpiration() int64 {\n\tif x != nil {\n\t\treturn x.Expiration\n\t}\n\treturn 0\n}\n\ntype CredentialVerificationServiceInitFlow struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *CredentialVerificationServiceInitFlow) Reset() {\n\t*x = CredentialVerificationServiceInitFlow{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[69]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *CredentialVerificationServiceInitFlow) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*CredentialVerificationServiceInitFlow) ProtoMessage() {}\n\nfunc (x *CredentialVerificationServiceInitFlow) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[69]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use CredentialVerificationServiceInitFlow.ProtoReflect.Descriptor instead.\nfunc (*CredentialVerificationServiceInitFlow) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{69}\n}\n\ntype CredentialVerificationServiceCompleteFlow struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *CredentialVerificationServiceCompleteFlow) Reset() {\n\t*x = CredentialVerificationServiceCompleteFlow{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[70]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *CredentialVerificationServiceCompleteFlow) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*CredentialVerificationServiceCompleteFlow) ProtoMessage() {}\n\nfunc (x *CredentialVerificationServiceCompleteFlow) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[70]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use CredentialVerificationServiceCompleteFlow.ProtoReflect.Descriptor instead.\nfunc (*CredentialVerificationServiceCompleteFlow) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{70}\n}\n\ntype VerifiedCredentialsList struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *VerifiedCredentialsList) Reset() {\n\t*x = VerifiedCredentialsList{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[71]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *VerifiedCredentialsList) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*VerifiedCredentialsList) ProtoMessage() {}\n\nfunc (x *VerifiedCredentialsList) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[71]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use VerifiedCredentialsList.ProtoReflect.Descriptor instead.\nfunc (*VerifiedCredentialsList) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{71}\n}\n\ntype ReplicationServiceRegisterGroup struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *ReplicationServiceRegisterGroup) Reset() {\n\t*x = ReplicationServiceRegisterGroup{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[72]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ReplicationServiceRegisterGroup) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ReplicationServiceRegisterGroup) ProtoMessage() {}\n\nfunc (x *ReplicationServiceRegisterGroup) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[72]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ReplicationServiceRegisterGroup.ProtoReflect.Descriptor instead.\nfunc (*ReplicationServiceRegisterGroup) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{72}\n}\n\ntype ReplicationServiceReplicateGroup struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *ReplicationServiceReplicateGroup) Reset() {\n\t*x = ReplicationServiceReplicateGroup{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[73]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ReplicationServiceReplicateGroup) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ReplicationServiceReplicateGroup) ProtoMessage() {}\n\nfunc (x *ReplicationServiceReplicateGroup) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[73]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ReplicationServiceReplicateGroup.ProtoReflect.Descriptor instead.\nfunc (*ReplicationServiceReplicateGroup) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{73}\n}\n\ntype SystemInfo struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *SystemInfo) Reset() {\n\t*x = SystemInfo{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[74]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *SystemInfo) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*SystemInfo) ProtoMessage() {}\n\nfunc (x *SystemInfo) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[74]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use SystemInfo.ProtoReflect.Descriptor instead.\nfunc (*SystemInfo) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{74}\n}\n\ntype PeerList struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *PeerList) Reset() {\n\t*x = PeerList{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[75]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *PeerList) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*PeerList) ProtoMessage() {}\n\nfunc (x *PeerList) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[75]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use PeerList.ProtoReflect.Descriptor instead.\nfunc (*PeerList) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{75}\n}\n\n// Progress define a generic object that can be used to display a progress bar for long-running actions.\ntype Progress struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tState     string  `protobuf:\"bytes,1,opt,name=state,proto3\" json:\"state,omitempty\"`\n\tDoing     string  `protobuf:\"bytes,2,opt,name=doing,proto3\" json:\"doing,omitempty\"`\n\tProgress  float32 `protobuf:\"fixed32,3,opt,name=progress,proto3\" json:\"progress,omitempty\"`\n\tCompleted uint64  `protobuf:\"varint,4,opt,name=completed,proto3\" json:\"completed,omitempty\"`\n\tTotal     uint64  `protobuf:\"varint,5,opt,name=total,proto3\" json:\"total,omitempty\"`\n\tDelay     uint64  `protobuf:\"varint,6,opt,name=delay,proto3\" json:\"delay,omitempty\"`\n}\n\nfunc (x *Progress) Reset() {\n\t*x = Progress{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[76]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *Progress) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Progress) ProtoMessage() {}\n\nfunc (x *Progress) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[76]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use Progress.ProtoReflect.Descriptor instead.\nfunc (*Progress) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{76}\n}\n\nfunc (x *Progress) GetState() string {\n\tif x != nil {\n\t\treturn x.State\n\t}\n\treturn \"\"\n}\n\nfunc (x *Progress) GetDoing() string {\n\tif x != nil {\n\t\treturn x.Doing\n\t}\n\treturn \"\"\n}\n\nfunc (x *Progress) GetProgress() float32 {\n\tif x != nil {\n\t\treturn x.Progress\n\t}\n\treturn 0\n}\n\nfunc (x *Progress) GetCompleted() uint64 {\n\tif x != nil {\n\t\treturn x.Completed\n\t}\n\treturn 0\n}\n\nfunc (x *Progress) GetTotal() uint64 {\n\tif x != nil {\n\t\treturn x.Total\n\t}\n\treturn 0\n}\n\nfunc (x *Progress) GetDelay() uint64 {\n\tif x != nil {\n\t\treturn x.Delay\n\t}\n\treturn 0\n}\n\ntype OutOfStoreMessage struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tCid              []byte `protobuf:\"bytes,1,opt,name=cid,proto3\" json:\"cid,omitempty\"`\n\tDevicePk         []byte `protobuf:\"bytes,2,opt,name=device_pk,json=devicePk,proto3\" json:\"device_pk,omitempty\"`\n\tCounter          uint64 `protobuf:\"fixed64,3,opt,name=counter,proto3\" json:\"counter,omitempty\"`\n\tSig              []byte `protobuf:\"bytes,4,opt,name=sig,proto3\" json:\"sig,omitempty\"`\n\tFlags            uint32 `protobuf:\"fixed32,5,opt,name=flags,proto3\" json:\"flags,omitempty\"`\n\tEncryptedPayload []byte `protobuf:\"bytes,6,opt,name=encrypted_payload,json=encryptedPayload,proto3\" json:\"encrypted_payload,omitempty\"`\n\tNonce            []byte `protobuf:\"bytes,7,opt,name=nonce,proto3\" json:\"nonce,omitempty\"`\n}\n\nfunc (x *OutOfStoreMessage) Reset() {\n\t*x = OutOfStoreMessage{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[77]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *OutOfStoreMessage) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*OutOfStoreMessage) ProtoMessage() {}\n\nfunc (x *OutOfStoreMessage) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[77]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use OutOfStoreMessage.ProtoReflect.Descriptor instead.\nfunc (*OutOfStoreMessage) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{77}\n}\n\nfunc (x *OutOfStoreMessage) GetCid() []byte {\n\tif x != nil {\n\t\treturn x.Cid\n\t}\n\treturn nil\n}\n\nfunc (x *OutOfStoreMessage) GetDevicePk() []byte {\n\tif x != nil {\n\t\treturn x.DevicePk\n\t}\n\treturn nil\n}\n\nfunc (x *OutOfStoreMessage) GetCounter() uint64 {\n\tif x != nil {\n\t\treturn x.Counter\n\t}\n\treturn 0\n}\n\nfunc (x *OutOfStoreMessage) GetSig() []byte {\n\tif x != nil {\n\t\treturn x.Sig\n\t}\n\treturn nil\n}\n\nfunc (x *OutOfStoreMessage) GetFlags() uint32 {\n\tif x != nil {\n\t\treturn x.Flags\n\t}\n\treturn 0\n}\n\nfunc (x *OutOfStoreMessage) GetEncryptedPayload() []byte {\n\tif x != nil {\n\t\treturn x.EncryptedPayload\n\t}\n\treturn nil\n}\n\nfunc (x *OutOfStoreMessage) GetNonce() []byte {\n\tif x != nil {\n\t\treturn x.Nonce\n\t}\n\treturn nil\n}\n\ntype OutOfStoreMessageEnvelope struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tNonce          []byte `protobuf:\"bytes,1,opt,name=nonce,proto3\" json:\"nonce,omitempty\"`\n\tBox            []byte `protobuf:\"bytes,2,opt,name=box,proto3\" json:\"box,omitempty\"`\n\tGroupReference []byte `protobuf:\"bytes,3,opt,name=group_reference,json=groupReference,proto3\" json:\"group_reference,omitempty\"`\n}\n\nfunc (x *OutOfStoreMessageEnvelope) Reset() {\n\t*x = OutOfStoreMessageEnvelope{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[78]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *OutOfStoreMessageEnvelope) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*OutOfStoreMessageEnvelope) ProtoMessage() {}\n\nfunc (x *OutOfStoreMessageEnvelope) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[78]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use OutOfStoreMessageEnvelope.ProtoReflect.Descriptor instead.\nfunc (*OutOfStoreMessageEnvelope) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{78}\n}\n\nfunc (x *OutOfStoreMessageEnvelope) GetNonce() []byte {\n\tif x != nil {\n\t\treturn x.Nonce\n\t}\n\treturn nil\n}\n\nfunc (x *OutOfStoreMessageEnvelope) GetBox() []byte {\n\tif x != nil {\n\t\treturn x.Box\n\t}\n\treturn nil\n}\n\nfunc (x *OutOfStoreMessageEnvelope) GetGroupReference() []byte {\n\tif x != nil {\n\t\treturn x.GroupReference\n\t}\n\treturn nil\n}\n\ntype OutOfStoreReceive struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *OutOfStoreReceive) Reset() {\n\t*x = OutOfStoreReceive{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[79]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *OutOfStoreReceive) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*OutOfStoreReceive) ProtoMessage() {}\n\nfunc (x *OutOfStoreReceive) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[79]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use OutOfStoreReceive.ProtoReflect.Descriptor instead.\nfunc (*OutOfStoreReceive) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{79}\n}\n\ntype OutOfStoreSeal struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *OutOfStoreSeal) Reset() {\n\t*x = OutOfStoreSeal{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[80]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *OutOfStoreSeal) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*OutOfStoreSeal) ProtoMessage() {}\n\nfunc (x *OutOfStoreSeal) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[80]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use OutOfStoreSeal.ProtoReflect.Descriptor instead.\nfunc (*OutOfStoreSeal) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{80}\n}\n\ntype AccountVerifiedCredentialRegistered struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// device_pk is the public key of the device sending the message\n\tDevicePk                []byte `protobuf:\"bytes,1,opt,name=device_pk,json=devicePk,proto3\" json:\"device_pk,omitempty\"`\n\tSignedIdentityPublicKey []byte `protobuf:\"bytes,2,opt,name=signed_identity_public_key,json=signedIdentityPublicKey,proto3\" json:\"signed_identity_public_key,omitempty\"`\n\tVerifiedCredential      string `protobuf:\"bytes,3,opt,name=verified_credential,json=verifiedCredential,proto3\" json:\"verified_credential,omitempty\"`\n\tRegistrationDate        int64  `protobuf:\"varint,4,opt,name=registration_date,json=registrationDate,proto3\" json:\"registration_date,omitempty\"`\n\tExpirationDate          int64  `protobuf:\"varint,5,opt,name=expiration_date,json=expirationDate,proto3\" json:\"expiration_date,omitempty\"`\n\tIdentifier              string `protobuf:\"bytes,6,opt,name=identifier,proto3\" json:\"identifier,omitempty\"`\n\tIssuer                  string `protobuf:\"bytes,7,opt,name=issuer,proto3\" json:\"issuer,omitempty\"`\n}\n\nfunc (x *AccountVerifiedCredentialRegistered) Reset() {\n\t*x = AccountVerifiedCredentialRegistered{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[81]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *AccountVerifiedCredentialRegistered) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*AccountVerifiedCredentialRegistered) ProtoMessage() {}\n\nfunc (x *AccountVerifiedCredentialRegistered) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[81]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use AccountVerifiedCredentialRegistered.ProtoReflect.Descriptor instead.\nfunc (*AccountVerifiedCredentialRegistered) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{81}\n}\n\nfunc (x *AccountVerifiedCredentialRegistered) GetDevicePk() []byte {\n\tif x != nil {\n\t\treturn x.DevicePk\n\t}\n\treturn nil\n}\n\nfunc (x *AccountVerifiedCredentialRegistered) GetSignedIdentityPublicKey() []byte {\n\tif x != nil {\n\t\treturn x.SignedIdentityPublicKey\n\t}\n\treturn nil\n}\n\nfunc (x *AccountVerifiedCredentialRegistered) GetVerifiedCredential() string {\n\tif x != nil {\n\t\treturn x.VerifiedCredential\n\t}\n\treturn \"\"\n}\n\nfunc (x *AccountVerifiedCredentialRegistered) GetRegistrationDate() int64 {\n\tif x != nil {\n\t\treturn x.RegistrationDate\n\t}\n\treturn 0\n}\n\nfunc (x *AccountVerifiedCredentialRegistered) GetExpirationDate() int64 {\n\tif x != nil {\n\t\treturn x.ExpirationDate\n\t}\n\treturn 0\n}\n\nfunc (x *AccountVerifiedCredentialRegistered) GetIdentifier() string {\n\tif x != nil {\n\t\treturn x.Identifier\n\t}\n\treturn \"\"\n}\n\nfunc (x *AccountVerifiedCredentialRegistered) GetIssuer() string {\n\tif x != nil {\n\t\treturn x.Issuer\n\t}\n\treturn \"\"\n}\n\ntype FirstLastCounters struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tFirst uint64 `protobuf:\"varint,1,opt,name=first,proto3\" json:\"first,omitempty\"`\n\tLast  uint64 `protobuf:\"varint,2,opt,name=last,proto3\" json:\"last,omitempty\"`\n}\n\nfunc (x *FirstLastCounters) Reset() {\n\t*x = FirstLastCounters{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[82]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *FirstLastCounters) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*FirstLastCounters) ProtoMessage() {}\n\nfunc (x *FirstLastCounters) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[82]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use FirstLastCounters.ProtoReflect.Descriptor instead.\nfunc (*FirstLastCounters) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{82}\n}\n\nfunc (x *FirstLastCounters) GetFirst() uint64 {\n\tif x != nil {\n\t\treturn x.First\n\t}\n\treturn 0\n}\n\nfunc (x *FirstLastCounters) GetLast() uint64 {\n\tif x != nil {\n\t\treturn x.Last\n\t}\n\treturn 0\n}\n\n// OrbitDBMessageHeads is the payload sent on orbitdb to share peer's heads\ntype OrbitDBMessageHeads struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// sealed box should contain encrypted Box\n\tSealedBox []byte `protobuf:\"bytes,2,opt,name=sealed_box,json=sealedBox,proto3\" json:\"sealed_box,omitempty\"`\n\t// current topic used\n\tRawRotation []byte `protobuf:\"bytes,3,opt,name=raw_rotation,json=rawRotation,proto3\" json:\"raw_rotation,omitempty\"`\n}\n\nfunc (x *OrbitDBMessageHeads) Reset() {\n\t*x = OrbitDBMessageHeads{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[83]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *OrbitDBMessageHeads) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*OrbitDBMessageHeads) ProtoMessage() {}\n\nfunc (x *OrbitDBMessageHeads) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[83]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use OrbitDBMessageHeads.ProtoReflect.Descriptor instead.\nfunc (*OrbitDBMessageHeads) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{83}\n}\n\nfunc (x *OrbitDBMessageHeads) GetSealedBox() []byte {\n\tif x != nil {\n\t\treturn x.SealedBox\n\t}\n\treturn nil\n}\n\nfunc (x *OrbitDBMessageHeads) GetRawRotation() []byte {\n\tif x != nil {\n\t\treturn x.RawRotation\n\t}\n\treturn nil\n}\n\ntype RefreshContactRequest struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *RefreshContactRequest) Reset() {\n\t*x = RefreshContactRequest{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[84]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *RefreshContactRequest) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*RefreshContactRequest) ProtoMessage() {}\n\nfunc (x *RefreshContactRequest) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[84]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use RefreshContactRequest.ProtoReflect.Descriptor instead.\nfunc (*RefreshContactRequest) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{84}\n}\n\ntype ServiceExportData_Request struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *ServiceExportData_Request) Reset() {\n\t*x = ServiceExportData_Request{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[86]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ServiceExportData_Request) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ServiceExportData_Request) ProtoMessage() {}\n\nfunc (x *ServiceExportData_Request) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[86]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ServiceExportData_Request.ProtoReflect.Descriptor instead.\nfunc (*ServiceExportData_Request) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{33, 0}\n}\n\ntype ServiceExportData_Reply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tExportedData []byte `protobuf:\"bytes,1,opt,name=exported_data,json=exportedData,proto3\" json:\"exported_data,omitempty\"`\n}\n\nfunc (x *ServiceExportData_Reply) Reset() {\n\t*x = ServiceExportData_Reply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[87]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ServiceExportData_Reply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ServiceExportData_Reply) ProtoMessage() {}\n\nfunc (x *ServiceExportData_Reply) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[87]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ServiceExportData_Reply.ProtoReflect.Descriptor instead.\nfunc (*ServiceExportData_Reply) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{33, 1}\n}\n\nfunc (x *ServiceExportData_Reply) GetExportedData() []byte {\n\tif x != nil {\n\t\treturn x.ExportedData\n\t}\n\treturn nil\n}\n\ntype ServiceGetConfiguration_Request struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *ServiceGetConfiguration_Request) Reset() {\n\t*x = ServiceGetConfiguration_Request{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[88]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ServiceGetConfiguration_Request) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ServiceGetConfiguration_Request) ProtoMessage() {}\n\nfunc (x *ServiceGetConfiguration_Request) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[88]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ServiceGetConfiguration_Request.ProtoReflect.Descriptor instead.\nfunc (*ServiceGetConfiguration_Request) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{34, 0}\n}\n\ntype ServiceGetConfiguration_Reply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// account_pk is the public key of the current account\n\tAccountPk []byte `protobuf:\"bytes,1,opt,name=account_pk,json=accountPk,proto3\" json:\"account_pk,omitempty\"`\n\t// device_pk is the public key of the current device\n\tDevicePk []byte `protobuf:\"bytes,2,opt,name=device_pk,json=devicePk,proto3\" json:\"device_pk,omitempty\"`\n\t// account_group_pk is the public key of the account group\n\tAccountGroupPk []byte `protobuf:\"bytes,3,opt,name=account_group_pk,json=accountGroupPk,proto3\" json:\"account_group_pk,omitempty\"`\n\t// peer_id is the peer ID of the current IPFS node\n\tPeerId string `protobuf:\"bytes,4,opt,name=peer_id,json=peerId,proto3\" json:\"peer_id,omitempty\"`\n\t// listeners is the list of swarm listening addresses of the current IPFS node\n\tListeners      []string                             `protobuf:\"bytes,5,rep,name=listeners,proto3\" json:\"listeners,omitempty\"`\n\tBleEnabled     ServiceGetConfiguration_SettingState `protobuf:\"varint,6,opt,name=ble_enabled,json=bleEnabled,proto3,enum=weshnet.protocol.v1.ServiceGetConfiguration_SettingState\" json:\"ble_enabled,omitempty\"`\n\tWifiP2PEnabled ServiceGetConfiguration_SettingState `protobuf:\"varint,7,opt,name=wifi_p2p_enabled,json=wifiP2pEnabled,proto3,enum=weshnet.protocol.v1.ServiceGetConfiguration_SettingState\" json:\"wifi_p2p_enabled,omitempty\"` // MultiPeerConnectivity for Darwin and Nearby for Android\n\tMdnsEnabled    ServiceGetConfiguration_SettingState `protobuf:\"varint,8,opt,name=mdns_enabled,json=mdnsEnabled,proto3,enum=weshnet.protocol.v1.ServiceGetConfiguration_SettingState\" json:\"mdns_enabled,omitempty\"`\n\tRelayEnabled   ServiceGetConfiguration_SettingState `protobuf:\"varint,9,opt,name=relay_enabled,json=relayEnabled,proto3,enum=weshnet.protocol.v1.ServiceGetConfiguration_SettingState\" json:\"relay_enabled,omitempty\"`\n}\n\nfunc (x *ServiceGetConfiguration_Reply) Reset() {\n\t*x = ServiceGetConfiguration_Reply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[89]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ServiceGetConfiguration_Reply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ServiceGetConfiguration_Reply) ProtoMessage() {}\n\nfunc (x *ServiceGetConfiguration_Reply) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[89]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ServiceGetConfiguration_Reply.ProtoReflect.Descriptor instead.\nfunc (*ServiceGetConfiguration_Reply) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{34, 1}\n}\n\nfunc (x *ServiceGetConfiguration_Reply) GetAccountPk() []byte {\n\tif x != nil {\n\t\treturn x.AccountPk\n\t}\n\treturn nil\n}\n\nfunc (x *ServiceGetConfiguration_Reply) GetDevicePk() []byte {\n\tif x != nil {\n\t\treturn x.DevicePk\n\t}\n\treturn nil\n}\n\nfunc (x *ServiceGetConfiguration_Reply) GetAccountGroupPk() []byte {\n\tif x != nil {\n\t\treturn x.AccountGroupPk\n\t}\n\treturn nil\n}\n\nfunc (x *ServiceGetConfiguration_Reply) GetPeerId() string {\n\tif x != nil {\n\t\treturn x.PeerId\n\t}\n\treturn \"\"\n}\n\nfunc (x *ServiceGetConfiguration_Reply) GetListeners() []string {\n\tif x != nil {\n\t\treturn x.Listeners\n\t}\n\treturn nil\n}\n\nfunc (x *ServiceGetConfiguration_Reply) GetBleEnabled() ServiceGetConfiguration_SettingState {\n\tif x != nil {\n\t\treturn x.BleEnabled\n\t}\n\treturn ServiceGetConfiguration_Unknown\n}\n\nfunc (x *ServiceGetConfiguration_Reply) GetWifiP2PEnabled() ServiceGetConfiguration_SettingState {\n\tif x != nil {\n\t\treturn x.WifiP2PEnabled\n\t}\n\treturn ServiceGetConfiguration_Unknown\n}\n\nfunc (x *ServiceGetConfiguration_Reply) GetMdnsEnabled() ServiceGetConfiguration_SettingState {\n\tif x != nil {\n\t\treturn x.MdnsEnabled\n\t}\n\treturn ServiceGetConfiguration_Unknown\n}\n\nfunc (x *ServiceGetConfiguration_Reply) GetRelayEnabled() ServiceGetConfiguration_SettingState {\n\tif x != nil {\n\t\treturn x.RelayEnabled\n\t}\n\treturn ServiceGetConfiguration_Unknown\n}\n\ntype ContactRequestReference_Request struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *ContactRequestReference_Request) Reset() {\n\t*x = ContactRequestReference_Request{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[90]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ContactRequestReference_Request) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ContactRequestReference_Request) ProtoMessage() {}\n\nfunc (x *ContactRequestReference_Request) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[90]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ContactRequestReference_Request.ProtoReflect.Descriptor instead.\nfunc (*ContactRequestReference_Request) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{35, 0}\n}\n\ntype ContactRequestReference_Reply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// public_rendezvous_seed is the rendezvous seed used by the current account\n\tPublicRendezvousSeed []byte `protobuf:\"bytes,1,opt,name=public_rendezvous_seed,json=publicRendezvousSeed,proto3\" json:\"public_rendezvous_seed,omitempty\"`\n\t// enabled indicates if incoming contact requests are enabled\n\tEnabled bool `protobuf:\"varint,2,opt,name=enabled,proto3\" json:\"enabled,omitempty\"`\n}\n\nfunc (x *ContactRequestReference_Reply) Reset() {\n\t*x = ContactRequestReference_Reply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[91]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ContactRequestReference_Reply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ContactRequestReference_Reply) ProtoMessage() {}\n\nfunc (x *ContactRequestReference_Reply) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[91]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ContactRequestReference_Reply.ProtoReflect.Descriptor instead.\nfunc (*ContactRequestReference_Reply) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{35, 1}\n}\n\nfunc (x *ContactRequestReference_Reply) GetPublicRendezvousSeed() []byte {\n\tif x != nil {\n\t\treturn x.PublicRendezvousSeed\n\t}\n\treturn nil\n}\n\nfunc (x *ContactRequestReference_Reply) GetEnabled() bool {\n\tif x != nil {\n\t\treturn x.Enabled\n\t}\n\treturn false\n}\n\ntype ContactRequestDisable_Request struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *ContactRequestDisable_Request) Reset() {\n\t*x = ContactRequestDisable_Request{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[92]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ContactRequestDisable_Request) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ContactRequestDisable_Request) ProtoMessage() {}\n\nfunc (x *ContactRequestDisable_Request) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[92]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ContactRequestDisable_Request.ProtoReflect.Descriptor instead.\nfunc (*ContactRequestDisable_Request) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{36, 0}\n}\n\ntype ContactRequestDisable_Reply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *ContactRequestDisable_Reply) Reset() {\n\t*x = ContactRequestDisable_Reply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[93]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ContactRequestDisable_Reply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ContactRequestDisable_Reply) ProtoMessage() {}\n\nfunc (x *ContactRequestDisable_Reply) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[93]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ContactRequestDisable_Reply.ProtoReflect.Descriptor instead.\nfunc (*ContactRequestDisable_Reply) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{36, 1}\n}\n\ntype ContactRequestEnable_Request struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *ContactRequestEnable_Request) Reset() {\n\t*x = ContactRequestEnable_Request{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[94]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ContactRequestEnable_Request) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ContactRequestEnable_Request) ProtoMessage() {}\n\nfunc (x *ContactRequestEnable_Request) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[94]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ContactRequestEnable_Request.ProtoReflect.Descriptor instead.\nfunc (*ContactRequestEnable_Request) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{37, 0}\n}\n\ntype ContactRequestEnable_Reply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// public_rendezvous_seed is the rendezvous seed used by the current account\n\tPublicRendezvousSeed []byte `protobuf:\"bytes,1,opt,name=public_rendezvous_seed,json=publicRendezvousSeed,proto3\" json:\"public_rendezvous_seed,omitempty\"`\n}\n\nfunc (x *ContactRequestEnable_Reply) Reset() {\n\t*x = ContactRequestEnable_Reply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[95]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ContactRequestEnable_Reply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ContactRequestEnable_Reply) ProtoMessage() {}\n\nfunc (x *ContactRequestEnable_Reply) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[95]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ContactRequestEnable_Reply.ProtoReflect.Descriptor instead.\nfunc (*ContactRequestEnable_Reply) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{37, 1}\n}\n\nfunc (x *ContactRequestEnable_Reply) GetPublicRendezvousSeed() []byte {\n\tif x != nil {\n\t\treturn x.PublicRendezvousSeed\n\t}\n\treturn nil\n}\n\ntype ContactRequestResetReference_Request struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *ContactRequestResetReference_Request) Reset() {\n\t*x = ContactRequestResetReference_Request{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[96]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ContactRequestResetReference_Request) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ContactRequestResetReference_Request) ProtoMessage() {}\n\nfunc (x *ContactRequestResetReference_Request) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[96]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ContactRequestResetReference_Request.ProtoReflect.Descriptor instead.\nfunc (*ContactRequestResetReference_Request) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{38, 0}\n}\n\ntype ContactRequestResetReference_Reply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// public_rendezvous_seed is the rendezvous seed used by the current account\n\tPublicRendezvousSeed []byte `protobuf:\"bytes,1,opt,name=public_rendezvous_seed,json=publicRendezvousSeed,proto3\" json:\"public_rendezvous_seed,omitempty\"`\n}\n\nfunc (x *ContactRequestResetReference_Reply) Reset() {\n\t*x = ContactRequestResetReference_Reply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[97]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ContactRequestResetReference_Reply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ContactRequestResetReference_Reply) ProtoMessage() {}\n\nfunc (x *ContactRequestResetReference_Reply) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[97]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ContactRequestResetReference_Reply.ProtoReflect.Descriptor instead.\nfunc (*ContactRequestResetReference_Reply) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{38, 1}\n}\n\nfunc (x *ContactRequestResetReference_Reply) GetPublicRendezvousSeed() []byte {\n\tif x != nil {\n\t\treturn x.PublicRendezvousSeed\n\t}\n\treturn nil\n}\n\ntype ContactRequestSend_Request struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// contact is a message describing how to connect to the other account\n\tContact *ShareableContact `protobuf:\"bytes,1,opt,name=contact,proto3\" json:\"contact,omitempty\"`\n\t// own_metadata is the identifying metadata that will be shared to the other account\n\tOwnMetadata []byte `protobuf:\"bytes,2,opt,name=own_metadata,json=ownMetadata,proto3\" json:\"own_metadata,omitempty\"`\n}\n\nfunc (x *ContactRequestSend_Request) Reset() {\n\t*x = ContactRequestSend_Request{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[98]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ContactRequestSend_Request) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ContactRequestSend_Request) ProtoMessage() {}\n\nfunc (x *ContactRequestSend_Request) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[98]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ContactRequestSend_Request.ProtoReflect.Descriptor instead.\nfunc (*ContactRequestSend_Request) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{39, 0}\n}\n\nfunc (x *ContactRequestSend_Request) GetContact() *ShareableContact {\n\tif x != nil {\n\t\treturn x.Contact\n\t}\n\treturn nil\n}\n\nfunc (x *ContactRequestSend_Request) GetOwnMetadata() []byte {\n\tif x != nil {\n\t\treturn x.OwnMetadata\n\t}\n\treturn nil\n}\n\ntype ContactRequestSend_Reply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *ContactRequestSend_Reply) Reset() {\n\t*x = ContactRequestSend_Reply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[99]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ContactRequestSend_Reply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ContactRequestSend_Reply) ProtoMessage() {}\n\nfunc (x *ContactRequestSend_Reply) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[99]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ContactRequestSend_Reply.ProtoReflect.Descriptor instead.\nfunc (*ContactRequestSend_Reply) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{39, 1}\n}\n\ntype ContactRequestAccept_Request struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// contact_pk is the identifier of the contact to accept the request from\n\tContactPk []byte `protobuf:\"bytes,1,opt,name=contact_pk,json=contactPk,proto3\" json:\"contact_pk,omitempty\"`\n}\n\nfunc (x *ContactRequestAccept_Request) Reset() {\n\t*x = ContactRequestAccept_Request{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[100]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ContactRequestAccept_Request) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ContactRequestAccept_Request) ProtoMessage() {}\n\nfunc (x *ContactRequestAccept_Request) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[100]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ContactRequestAccept_Request.ProtoReflect.Descriptor instead.\nfunc (*ContactRequestAccept_Request) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{40, 0}\n}\n\nfunc (x *ContactRequestAccept_Request) GetContactPk() []byte {\n\tif x != nil {\n\t\treturn x.ContactPk\n\t}\n\treturn nil\n}\n\ntype ContactRequestAccept_Reply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *ContactRequestAccept_Reply) Reset() {\n\t*x = ContactRequestAccept_Reply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[101]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ContactRequestAccept_Reply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ContactRequestAccept_Reply) ProtoMessage() {}\n\nfunc (x *ContactRequestAccept_Reply) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[101]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ContactRequestAccept_Reply.ProtoReflect.Descriptor instead.\nfunc (*ContactRequestAccept_Reply) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{40, 1}\n}\n\ntype ContactRequestDiscard_Request struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// contact_pk is the identifier of the contact to ignore the request from\n\tContactPk []byte `protobuf:\"bytes,1,opt,name=contact_pk,json=contactPk,proto3\" json:\"contact_pk,omitempty\"`\n}\n\nfunc (x *ContactRequestDiscard_Request) Reset() {\n\t*x = ContactRequestDiscard_Request{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[102]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ContactRequestDiscard_Request) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ContactRequestDiscard_Request) ProtoMessage() {}\n\nfunc (x *ContactRequestDiscard_Request) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[102]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ContactRequestDiscard_Request.ProtoReflect.Descriptor instead.\nfunc (*ContactRequestDiscard_Request) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{41, 0}\n}\n\nfunc (x *ContactRequestDiscard_Request) GetContactPk() []byte {\n\tif x != nil {\n\t\treturn x.ContactPk\n\t}\n\treturn nil\n}\n\ntype ContactRequestDiscard_Reply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *ContactRequestDiscard_Reply) Reset() {\n\t*x = ContactRequestDiscard_Reply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[103]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ContactRequestDiscard_Reply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ContactRequestDiscard_Reply) ProtoMessage() {}\n\nfunc (x *ContactRequestDiscard_Reply) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[103]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ContactRequestDiscard_Reply.ProtoReflect.Descriptor instead.\nfunc (*ContactRequestDiscard_Reply) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{41, 1}\n}\n\ntype ShareContact_Request struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *ShareContact_Request) Reset() {\n\t*x = ShareContact_Request{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[104]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ShareContact_Request) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ShareContact_Request) ProtoMessage() {}\n\nfunc (x *ShareContact_Request) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[104]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ShareContact_Request.ProtoReflect.Descriptor instead.\nfunc (*ShareContact_Request) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{42, 0}\n}\n\ntype ShareContact_Reply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// encoded_contact is the Protobuf encoding of the ShareableContact. You can further encode the bytes for sharing, such as base58 or QR code.\n\tEncodedContact []byte `protobuf:\"bytes,1,opt,name=encoded_contact,json=encodedContact,proto3\" json:\"encoded_contact,omitempty\"`\n}\n\nfunc (x *ShareContact_Reply) Reset() {\n\t*x = ShareContact_Reply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[105]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ShareContact_Reply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ShareContact_Reply) ProtoMessage() {}\n\nfunc (x *ShareContact_Reply) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[105]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ShareContact_Reply.ProtoReflect.Descriptor instead.\nfunc (*ShareContact_Reply) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{42, 1}\n}\n\nfunc (x *ShareContact_Reply) GetEncodedContact() []byte {\n\tif x != nil {\n\t\treturn x.EncodedContact\n\t}\n\treturn nil\n}\n\ntype DecodeContact_Request struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// encoded_contact is the Protobuf encoding of the shareable contact (as returned by ShareContact).\n\tEncodedContact []byte `protobuf:\"bytes,1,opt,name=encoded_contact,json=encodedContact,proto3\" json:\"encoded_contact,omitempty\"`\n}\n\nfunc (x *DecodeContact_Request) Reset() {\n\t*x = DecodeContact_Request{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[106]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *DecodeContact_Request) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*DecodeContact_Request) ProtoMessage() {}\n\nfunc (x *DecodeContact_Request) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[106]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use DecodeContact_Request.ProtoReflect.Descriptor instead.\nfunc (*DecodeContact_Request) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{43, 0}\n}\n\nfunc (x *DecodeContact_Request) GetEncodedContact() []byte {\n\tif x != nil {\n\t\treturn x.EncodedContact\n\t}\n\treturn nil\n}\n\ntype DecodeContact_Reply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// shareable_contact is the decoded shareable contact.\n\tContact *ShareableContact `protobuf:\"bytes,1,opt,name=contact,proto3\" json:\"contact,omitempty\"`\n}\n\nfunc (x *DecodeContact_Reply) Reset() {\n\t*x = DecodeContact_Reply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[107]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *DecodeContact_Reply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*DecodeContact_Reply) ProtoMessage() {}\n\nfunc (x *DecodeContact_Reply) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[107]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use DecodeContact_Reply.ProtoReflect.Descriptor instead.\nfunc (*DecodeContact_Reply) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{43, 1}\n}\n\nfunc (x *DecodeContact_Reply) GetContact() *ShareableContact {\n\tif x != nil {\n\t\treturn x.Contact\n\t}\n\treturn nil\n}\n\ntype ContactBlock_Request struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// contact_pk is the identifier of the contact to block\n\tContactPk []byte `protobuf:\"bytes,1,opt,name=contact_pk,json=contactPk,proto3\" json:\"contact_pk,omitempty\"`\n}\n\nfunc (x *ContactBlock_Request) Reset() {\n\t*x = ContactBlock_Request{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[108]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ContactBlock_Request) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ContactBlock_Request) ProtoMessage() {}\n\nfunc (x *ContactBlock_Request) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[108]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ContactBlock_Request.ProtoReflect.Descriptor instead.\nfunc (*ContactBlock_Request) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{44, 0}\n}\n\nfunc (x *ContactBlock_Request) GetContactPk() []byte {\n\tif x != nil {\n\t\treturn x.ContactPk\n\t}\n\treturn nil\n}\n\ntype ContactBlock_Reply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *ContactBlock_Reply) Reset() {\n\t*x = ContactBlock_Reply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[109]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ContactBlock_Reply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ContactBlock_Reply) ProtoMessage() {}\n\nfunc (x *ContactBlock_Reply) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[109]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ContactBlock_Reply.ProtoReflect.Descriptor instead.\nfunc (*ContactBlock_Reply) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{44, 1}\n}\n\ntype ContactUnblock_Request struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// contact_pk is the identifier of the contact to unblock\n\tContactPk []byte `protobuf:\"bytes,1,opt,name=contact_pk,json=contactPk,proto3\" json:\"contact_pk,omitempty\"`\n}\n\nfunc (x *ContactUnblock_Request) Reset() {\n\t*x = ContactUnblock_Request{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[110]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ContactUnblock_Request) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ContactUnblock_Request) ProtoMessage() {}\n\nfunc (x *ContactUnblock_Request) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[110]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ContactUnblock_Request.ProtoReflect.Descriptor instead.\nfunc (*ContactUnblock_Request) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{45, 0}\n}\n\nfunc (x *ContactUnblock_Request) GetContactPk() []byte {\n\tif x != nil {\n\t\treturn x.ContactPk\n\t}\n\treturn nil\n}\n\ntype ContactUnblock_Reply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *ContactUnblock_Reply) Reset() {\n\t*x = ContactUnblock_Reply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[111]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ContactUnblock_Reply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ContactUnblock_Reply) ProtoMessage() {}\n\nfunc (x *ContactUnblock_Reply) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[111]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ContactUnblock_Reply.ProtoReflect.Descriptor instead.\nfunc (*ContactUnblock_Reply) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{45, 1}\n}\n\ntype ContactAliasKeySend_Request struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// contact_pk is the identifier of the contact to send the alias public key to\n\tGroupPk []byte `protobuf:\"bytes,1,opt,name=group_pk,json=groupPk,proto3\" json:\"group_pk,omitempty\"`\n}\n\nfunc (x *ContactAliasKeySend_Request) Reset() {\n\t*x = ContactAliasKeySend_Request{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[112]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ContactAliasKeySend_Request) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ContactAliasKeySend_Request) ProtoMessage() {}\n\nfunc (x *ContactAliasKeySend_Request) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[112]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ContactAliasKeySend_Request.ProtoReflect.Descriptor instead.\nfunc (*ContactAliasKeySend_Request) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{46, 0}\n}\n\nfunc (x *ContactAliasKeySend_Request) GetGroupPk() []byte {\n\tif x != nil {\n\t\treturn x.GroupPk\n\t}\n\treturn nil\n}\n\ntype ContactAliasKeySend_Reply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *ContactAliasKeySend_Reply) Reset() {\n\t*x = ContactAliasKeySend_Reply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[113]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ContactAliasKeySend_Reply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ContactAliasKeySend_Reply) ProtoMessage() {}\n\nfunc (x *ContactAliasKeySend_Reply) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[113]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ContactAliasKeySend_Reply.ProtoReflect.Descriptor instead.\nfunc (*ContactAliasKeySend_Reply) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{46, 1}\n}\n\ntype MultiMemberGroupCreate_Request struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *MultiMemberGroupCreate_Request) Reset() {\n\t*x = MultiMemberGroupCreate_Request{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[114]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *MultiMemberGroupCreate_Request) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*MultiMemberGroupCreate_Request) ProtoMessage() {}\n\nfunc (x *MultiMemberGroupCreate_Request) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[114]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use MultiMemberGroupCreate_Request.ProtoReflect.Descriptor instead.\nfunc (*MultiMemberGroupCreate_Request) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{47, 0}\n}\n\ntype MultiMemberGroupCreate_Reply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// group_pk is the identifier of the newly created group\n\tGroupPk []byte `protobuf:\"bytes,1,opt,name=group_pk,json=groupPk,proto3\" json:\"group_pk,omitempty\"`\n}\n\nfunc (x *MultiMemberGroupCreate_Reply) Reset() {\n\t*x = MultiMemberGroupCreate_Reply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[115]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *MultiMemberGroupCreate_Reply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*MultiMemberGroupCreate_Reply) ProtoMessage() {}\n\nfunc (x *MultiMemberGroupCreate_Reply) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[115]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use MultiMemberGroupCreate_Reply.ProtoReflect.Descriptor instead.\nfunc (*MultiMemberGroupCreate_Reply) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{47, 1}\n}\n\nfunc (x *MultiMemberGroupCreate_Reply) GetGroupPk() []byte {\n\tif x != nil {\n\t\treturn x.GroupPk\n\t}\n\treturn nil\n}\n\ntype MultiMemberGroupJoin_Request struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// group is the information of the group to join\n\tGroup *Group `protobuf:\"bytes,1,opt,name=group,proto3\" json:\"group,omitempty\"`\n}\n\nfunc (x *MultiMemberGroupJoin_Request) Reset() {\n\t*x = MultiMemberGroupJoin_Request{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[116]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *MultiMemberGroupJoin_Request) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*MultiMemberGroupJoin_Request) ProtoMessage() {}\n\nfunc (x *MultiMemberGroupJoin_Request) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[116]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use MultiMemberGroupJoin_Request.ProtoReflect.Descriptor instead.\nfunc (*MultiMemberGroupJoin_Request) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{48, 0}\n}\n\nfunc (x *MultiMemberGroupJoin_Request) GetGroup() *Group {\n\tif x != nil {\n\t\treturn x.Group\n\t}\n\treturn nil\n}\n\ntype MultiMemberGroupJoin_Reply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *MultiMemberGroupJoin_Reply) Reset() {\n\t*x = MultiMemberGroupJoin_Reply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[117]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *MultiMemberGroupJoin_Reply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*MultiMemberGroupJoin_Reply) ProtoMessage() {}\n\nfunc (x *MultiMemberGroupJoin_Reply) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[117]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use MultiMemberGroupJoin_Reply.ProtoReflect.Descriptor instead.\nfunc (*MultiMemberGroupJoin_Reply) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{48, 1}\n}\n\ntype MultiMemberGroupLeave_Request struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tGroupPk []byte `protobuf:\"bytes,1,opt,name=group_pk,json=groupPk,proto3\" json:\"group_pk,omitempty\"`\n}\n\nfunc (x *MultiMemberGroupLeave_Request) Reset() {\n\t*x = MultiMemberGroupLeave_Request{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[118]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *MultiMemberGroupLeave_Request) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*MultiMemberGroupLeave_Request) ProtoMessage() {}\n\nfunc (x *MultiMemberGroupLeave_Request) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[118]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use MultiMemberGroupLeave_Request.ProtoReflect.Descriptor instead.\nfunc (*MultiMemberGroupLeave_Request) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{49, 0}\n}\n\nfunc (x *MultiMemberGroupLeave_Request) GetGroupPk() []byte {\n\tif x != nil {\n\t\treturn x.GroupPk\n\t}\n\treturn nil\n}\n\ntype MultiMemberGroupLeave_Reply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *MultiMemberGroupLeave_Reply) Reset() {\n\t*x = MultiMemberGroupLeave_Reply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[119]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *MultiMemberGroupLeave_Reply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*MultiMemberGroupLeave_Reply) ProtoMessage() {}\n\nfunc (x *MultiMemberGroupLeave_Reply) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[119]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use MultiMemberGroupLeave_Reply.ProtoReflect.Descriptor instead.\nfunc (*MultiMemberGroupLeave_Reply) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{49, 1}\n}\n\ntype MultiMemberGroupAliasResolverDisclose_Request struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// group_pk is the identifier of the group\n\tGroupPk []byte `protobuf:\"bytes,1,opt,name=group_pk,json=groupPk,proto3\" json:\"group_pk,omitempty\"`\n}\n\nfunc (x *MultiMemberGroupAliasResolverDisclose_Request) Reset() {\n\t*x = MultiMemberGroupAliasResolverDisclose_Request{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[120]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *MultiMemberGroupAliasResolverDisclose_Request) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*MultiMemberGroupAliasResolverDisclose_Request) ProtoMessage() {}\n\nfunc (x *MultiMemberGroupAliasResolverDisclose_Request) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[120]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use MultiMemberGroupAliasResolverDisclose_Request.ProtoReflect.Descriptor instead.\nfunc (*MultiMemberGroupAliasResolverDisclose_Request) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{50, 0}\n}\n\nfunc (x *MultiMemberGroupAliasResolverDisclose_Request) GetGroupPk() []byte {\n\tif x != nil {\n\t\treturn x.GroupPk\n\t}\n\treturn nil\n}\n\ntype MultiMemberGroupAliasResolverDisclose_Reply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *MultiMemberGroupAliasResolverDisclose_Reply) Reset() {\n\t*x = MultiMemberGroupAliasResolverDisclose_Reply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[121]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *MultiMemberGroupAliasResolverDisclose_Reply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*MultiMemberGroupAliasResolverDisclose_Reply) ProtoMessage() {}\n\nfunc (x *MultiMemberGroupAliasResolverDisclose_Reply) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[121]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use MultiMemberGroupAliasResolverDisclose_Reply.ProtoReflect.Descriptor instead.\nfunc (*MultiMemberGroupAliasResolverDisclose_Reply) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{50, 1}\n}\n\ntype MultiMemberGroupAdminRoleGrant_Request struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// group_pk is the identifier of the group\n\tGroupPk []byte `protobuf:\"bytes,1,opt,name=group_pk,json=groupPk,proto3\" json:\"group_pk,omitempty\"`\n\t// member_pk is the identifier of the member which will be granted the admin role\n\tMemberPk []byte `protobuf:\"bytes,2,opt,name=member_pk,json=memberPk,proto3\" json:\"member_pk,omitempty\"`\n}\n\nfunc (x *MultiMemberGroupAdminRoleGrant_Request) Reset() {\n\t*x = MultiMemberGroupAdminRoleGrant_Request{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[122]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *MultiMemberGroupAdminRoleGrant_Request) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*MultiMemberGroupAdminRoleGrant_Request) ProtoMessage() {}\n\nfunc (x *MultiMemberGroupAdminRoleGrant_Request) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[122]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use MultiMemberGroupAdminRoleGrant_Request.ProtoReflect.Descriptor instead.\nfunc (*MultiMemberGroupAdminRoleGrant_Request) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{51, 0}\n}\n\nfunc (x *MultiMemberGroupAdminRoleGrant_Request) GetGroupPk() []byte {\n\tif x != nil {\n\t\treturn x.GroupPk\n\t}\n\treturn nil\n}\n\nfunc (x *MultiMemberGroupAdminRoleGrant_Request) GetMemberPk() []byte {\n\tif x != nil {\n\t\treturn x.MemberPk\n\t}\n\treturn nil\n}\n\ntype MultiMemberGroupAdminRoleGrant_Reply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *MultiMemberGroupAdminRoleGrant_Reply) Reset() {\n\t*x = MultiMemberGroupAdminRoleGrant_Reply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[123]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *MultiMemberGroupAdminRoleGrant_Reply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*MultiMemberGroupAdminRoleGrant_Reply) ProtoMessage() {}\n\nfunc (x *MultiMemberGroupAdminRoleGrant_Reply) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[123]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use MultiMemberGroupAdminRoleGrant_Reply.ProtoReflect.Descriptor instead.\nfunc (*MultiMemberGroupAdminRoleGrant_Reply) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{51, 1}\n}\n\ntype MultiMemberGroupInvitationCreate_Request struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// group_pk is the identifier of the group\n\tGroupPk []byte `protobuf:\"bytes,1,opt,name=group_pk,json=groupPk,proto3\" json:\"group_pk,omitempty\"`\n}\n\nfunc (x *MultiMemberGroupInvitationCreate_Request) Reset() {\n\t*x = MultiMemberGroupInvitationCreate_Request{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[124]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *MultiMemberGroupInvitationCreate_Request) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*MultiMemberGroupInvitationCreate_Request) ProtoMessage() {}\n\nfunc (x *MultiMemberGroupInvitationCreate_Request) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[124]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use MultiMemberGroupInvitationCreate_Request.ProtoReflect.Descriptor instead.\nfunc (*MultiMemberGroupInvitationCreate_Request) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{52, 0}\n}\n\nfunc (x *MultiMemberGroupInvitationCreate_Request) GetGroupPk() []byte {\n\tif x != nil {\n\t\treturn x.GroupPk\n\t}\n\treturn nil\n}\n\ntype MultiMemberGroupInvitationCreate_Reply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// group is the invitation to the group\n\tGroup *Group `protobuf:\"bytes,1,opt,name=group,proto3\" json:\"group,omitempty\"`\n}\n\nfunc (x *MultiMemberGroupInvitationCreate_Reply) Reset() {\n\t*x = MultiMemberGroupInvitationCreate_Reply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[125]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *MultiMemberGroupInvitationCreate_Reply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*MultiMemberGroupInvitationCreate_Reply) ProtoMessage() {}\n\nfunc (x *MultiMemberGroupInvitationCreate_Reply) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[125]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use MultiMemberGroupInvitationCreate_Reply.ProtoReflect.Descriptor instead.\nfunc (*MultiMemberGroupInvitationCreate_Reply) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{52, 1}\n}\n\nfunc (x *MultiMemberGroupInvitationCreate_Reply) GetGroup() *Group {\n\tif x != nil {\n\t\treturn x.Group\n\t}\n\treturn nil\n}\n\ntype AppMetadataSend_Request struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// group_pk is the identifier of the group\n\tGroupPk []byte `protobuf:\"bytes,1,opt,name=group_pk,json=groupPk,proto3\" json:\"group_pk,omitempty\"`\n\t// payload is the payload to send\n\tPayload []byte `protobuf:\"bytes,2,opt,name=payload,proto3\" json:\"payload,omitempty\"`\n}\n\nfunc (x *AppMetadataSend_Request) Reset() {\n\t*x = AppMetadataSend_Request{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[126]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *AppMetadataSend_Request) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*AppMetadataSend_Request) ProtoMessage() {}\n\nfunc (x *AppMetadataSend_Request) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[126]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use AppMetadataSend_Request.ProtoReflect.Descriptor instead.\nfunc (*AppMetadataSend_Request) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{53, 0}\n}\n\nfunc (x *AppMetadataSend_Request) GetGroupPk() []byte {\n\tif x != nil {\n\t\treturn x.GroupPk\n\t}\n\treturn nil\n}\n\nfunc (x *AppMetadataSend_Request) GetPayload() []byte {\n\tif x != nil {\n\t\treturn x.Payload\n\t}\n\treturn nil\n}\n\ntype AppMetadataSend_Reply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tCid []byte `protobuf:\"bytes,1,opt,name=cid,proto3\" json:\"cid,omitempty\"`\n}\n\nfunc (x *AppMetadataSend_Reply) Reset() {\n\t*x = AppMetadataSend_Reply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[127]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *AppMetadataSend_Reply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*AppMetadataSend_Reply) ProtoMessage() {}\n\nfunc (x *AppMetadataSend_Reply) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[127]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use AppMetadataSend_Reply.ProtoReflect.Descriptor instead.\nfunc (*AppMetadataSend_Reply) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{53, 1}\n}\n\nfunc (x *AppMetadataSend_Reply) GetCid() []byte {\n\tif x != nil {\n\t\treturn x.Cid\n\t}\n\treturn nil\n}\n\ntype AppMessageSend_Request struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// group_pk is the identifier of the group\n\tGroupPk []byte `protobuf:\"bytes,1,opt,name=group_pk,json=groupPk,proto3\" json:\"group_pk,omitempty\"`\n\t// payload is the payload to send\n\tPayload []byte `protobuf:\"bytes,2,opt,name=payload,proto3\" json:\"payload,omitempty\"`\n}\n\nfunc (x *AppMessageSend_Request) Reset() {\n\t*x = AppMessageSend_Request{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[128]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *AppMessageSend_Request) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*AppMessageSend_Request) ProtoMessage() {}\n\nfunc (x *AppMessageSend_Request) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[128]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use AppMessageSend_Request.ProtoReflect.Descriptor instead.\nfunc (*AppMessageSend_Request) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{54, 0}\n}\n\nfunc (x *AppMessageSend_Request) GetGroupPk() []byte {\n\tif x != nil {\n\t\treturn x.GroupPk\n\t}\n\treturn nil\n}\n\nfunc (x *AppMessageSend_Request) GetPayload() []byte {\n\tif x != nil {\n\t\treturn x.Payload\n\t}\n\treturn nil\n}\n\ntype AppMessageSend_Reply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tCid []byte `protobuf:\"bytes,1,opt,name=cid,proto3\" json:\"cid,omitempty\"`\n}\n\nfunc (x *AppMessageSend_Reply) Reset() {\n\t*x = AppMessageSend_Reply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[129]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *AppMessageSend_Reply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*AppMessageSend_Reply) ProtoMessage() {}\n\nfunc (x *AppMessageSend_Reply) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[129]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use AppMessageSend_Reply.ProtoReflect.Descriptor instead.\nfunc (*AppMessageSend_Reply) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{54, 1}\n}\n\nfunc (x *AppMessageSend_Reply) GetCid() []byte {\n\tif x != nil {\n\t\treturn x.Cid\n\t}\n\treturn nil\n}\n\ntype GroupMetadataList_Request struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// group_pk is the identifier of the group\n\tGroupPk []byte `protobuf:\"bytes,1,opt,name=group_pk,json=groupPk,proto3\" json:\"group_pk,omitempty\"`\n\t// since is the lower ID bound used to filter events\n\t// if not set, will return events since the beginning\n\tSinceId []byte `protobuf:\"bytes,2,opt,name=since_id,json=sinceId,proto3\" json:\"since_id,omitempty\"`\n\t// since_now will list only new event to come\n\t// since_id must not be set\n\tSinceNow bool `protobuf:\"varint,3,opt,name=since_now,json=sinceNow,proto3\" json:\"since_now,omitempty\"`\n\t// until is the upper ID bound used to filter events\n\t// if not set, will subscribe to new events to come\n\tUntilId []byte `protobuf:\"bytes,4,opt,name=until_id,json=untilId,proto3\" json:\"until_id,omitempty\"`\n\t// until_now will not list new event to come\n\t// until_id must not be set\n\tUntilNow bool `protobuf:\"varint,5,opt,name=until_now,json=untilNow,proto3\" json:\"until_now,omitempty\"`\n\t// reverse_order indicates whether the previous events should be returned in\n\t// reverse chronological order\n\tReverseOrder bool `protobuf:\"varint,6,opt,name=reverse_order,json=reverseOrder,proto3\" json:\"reverse_order,omitempty\"`\n}\n\nfunc (x *GroupMetadataList_Request) Reset() {\n\t*x = GroupMetadataList_Request{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[130]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *GroupMetadataList_Request) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*GroupMetadataList_Request) ProtoMessage() {}\n\nfunc (x *GroupMetadataList_Request) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[130]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use GroupMetadataList_Request.ProtoReflect.Descriptor instead.\nfunc (*GroupMetadataList_Request) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{57, 0}\n}\n\nfunc (x *GroupMetadataList_Request) GetGroupPk() []byte {\n\tif x != nil {\n\t\treturn x.GroupPk\n\t}\n\treturn nil\n}\n\nfunc (x *GroupMetadataList_Request) GetSinceId() []byte {\n\tif x != nil {\n\t\treturn x.SinceId\n\t}\n\treturn nil\n}\n\nfunc (x *GroupMetadataList_Request) GetSinceNow() bool {\n\tif x != nil {\n\t\treturn x.SinceNow\n\t}\n\treturn false\n}\n\nfunc (x *GroupMetadataList_Request) GetUntilId() []byte {\n\tif x != nil {\n\t\treturn x.UntilId\n\t}\n\treturn nil\n}\n\nfunc (x *GroupMetadataList_Request) GetUntilNow() bool {\n\tif x != nil {\n\t\treturn x.UntilNow\n\t}\n\treturn false\n}\n\nfunc (x *GroupMetadataList_Request) GetReverseOrder() bool {\n\tif x != nil {\n\t\treturn x.ReverseOrder\n\t}\n\treturn false\n}\n\ntype GroupMessageList_Request struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// group_pk is the identifier of the group\n\tGroupPk []byte `protobuf:\"bytes,1,opt,name=group_pk,json=groupPk,proto3\" json:\"group_pk,omitempty\"`\n\t// since is the lower ID bound used to filter events\n\t// if not set, will return events since the beginning\n\tSinceId []byte `protobuf:\"bytes,2,opt,name=since_id,json=sinceId,proto3\" json:\"since_id,omitempty\"`\n\t// since_now will list only new event to come\n\t// since_id must not be set\n\tSinceNow bool `protobuf:\"varint,3,opt,name=since_now,json=sinceNow,proto3\" json:\"since_now,omitempty\"`\n\t// until is the upper ID bound used to filter events\n\t// if not set, will subscribe to new events to come\n\tUntilId []byte `protobuf:\"bytes,4,opt,name=until_id,json=untilId,proto3\" json:\"until_id,omitempty\"`\n\t// until_now will not list new event to come\n\t// until_id must not be set\n\tUntilNow bool `protobuf:\"varint,5,opt,name=until_now,json=untilNow,proto3\" json:\"until_now,omitempty\"`\n\t// reverse_order indicates whether the previous events should be returned in\n\t// reverse chronological order\n\tReverseOrder bool `protobuf:\"varint,6,opt,name=reverse_order,json=reverseOrder,proto3\" json:\"reverse_order,omitempty\"`\n}\n\nfunc (x *GroupMessageList_Request) Reset() {\n\t*x = GroupMessageList_Request{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[131]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *GroupMessageList_Request) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*GroupMessageList_Request) ProtoMessage() {}\n\nfunc (x *GroupMessageList_Request) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[131]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use GroupMessageList_Request.ProtoReflect.Descriptor instead.\nfunc (*GroupMessageList_Request) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{58, 0}\n}\n\nfunc (x *GroupMessageList_Request) GetGroupPk() []byte {\n\tif x != nil {\n\t\treturn x.GroupPk\n\t}\n\treturn nil\n}\n\nfunc (x *GroupMessageList_Request) GetSinceId() []byte {\n\tif x != nil {\n\t\treturn x.SinceId\n\t}\n\treturn nil\n}\n\nfunc (x *GroupMessageList_Request) GetSinceNow() bool {\n\tif x != nil {\n\t\treturn x.SinceNow\n\t}\n\treturn false\n}\n\nfunc (x *GroupMessageList_Request) GetUntilId() []byte {\n\tif x != nil {\n\t\treturn x.UntilId\n\t}\n\treturn nil\n}\n\nfunc (x *GroupMessageList_Request) GetUntilNow() bool {\n\tif x != nil {\n\t\treturn x.UntilNow\n\t}\n\treturn false\n}\n\nfunc (x *GroupMessageList_Request) GetReverseOrder() bool {\n\tif x != nil {\n\t\treturn x.ReverseOrder\n\t}\n\treturn false\n}\n\ntype GroupInfo_Request struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// group_pk is the identifier of the group\n\tGroupPk []byte `protobuf:\"bytes,1,opt,name=group_pk,json=groupPk,proto3\" json:\"group_pk,omitempty\"`\n\t// contact_pk is the identifier of the contact\n\tContactPk []byte `protobuf:\"bytes,2,opt,name=contact_pk,json=contactPk,proto3\" json:\"contact_pk,omitempty\"`\n}\n\nfunc (x *GroupInfo_Request) Reset() {\n\t*x = GroupInfo_Request{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[132]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *GroupInfo_Request) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*GroupInfo_Request) ProtoMessage() {}\n\nfunc (x *GroupInfo_Request) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[132]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use GroupInfo_Request.ProtoReflect.Descriptor instead.\nfunc (*GroupInfo_Request) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{59, 0}\n}\n\nfunc (x *GroupInfo_Request) GetGroupPk() []byte {\n\tif x != nil {\n\t\treturn x.GroupPk\n\t}\n\treturn nil\n}\n\nfunc (x *GroupInfo_Request) GetContactPk() []byte {\n\tif x != nil {\n\t\treturn x.ContactPk\n\t}\n\treturn nil\n}\n\ntype GroupInfo_Reply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// group is the group invitation, containing the group pk and its type\n\tGroup *Group `protobuf:\"bytes,1,opt,name=group,proto3\" json:\"group,omitempty\"`\n\t// member_pk is the identifier of the current member in the group\n\tMemberPk []byte `protobuf:\"bytes,2,opt,name=member_pk,json=memberPk,proto3\" json:\"member_pk,omitempty\"`\n\t// device_pk is the identifier of the current device in the group\n\tDevicePk []byte `protobuf:\"bytes,3,opt,name=device_pk,json=devicePk,proto3\" json:\"device_pk,omitempty\"`\n}\n\nfunc (x *GroupInfo_Reply) Reset() {\n\t*x = GroupInfo_Reply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[133]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *GroupInfo_Reply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*GroupInfo_Reply) ProtoMessage() {}\n\nfunc (x *GroupInfo_Reply) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[133]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use GroupInfo_Reply.ProtoReflect.Descriptor instead.\nfunc (*GroupInfo_Reply) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{59, 1}\n}\n\nfunc (x *GroupInfo_Reply) GetGroup() *Group {\n\tif x != nil {\n\t\treturn x.Group\n\t}\n\treturn nil\n}\n\nfunc (x *GroupInfo_Reply) GetMemberPk() []byte {\n\tif x != nil {\n\t\treturn x.MemberPk\n\t}\n\treturn nil\n}\n\nfunc (x *GroupInfo_Reply) GetDevicePk() []byte {\n\tif x != nil {\n\t\treturn x.DevicePk\n\t}\n\treturn nil\n}\n\ntype ActivateGroup_Request struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// group_pk is the identifier of the group\n\tGroupPk []byte `protobuf:\"bytes,1,opt,name=group_pk,json=groupPk,proto3\" json:\"group_pk,omitempty\"`\n\t// local_only will open the group without enabling network interactions\n\t// with other members\n\tLocalOnly bool `protobuf:\"varint,2,opt,name=local_only,json=localOnly,proto3\" json:\"local_only,omitempty\"`\n}\n\nfunc (x *ActivateGroup_Request) Reset() {\n\t*x = ActivateGroup_Request{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[134]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ActivateGroup_Request) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ActivateGroup_Request) ProtoMessage() {}\n\nfunc (x *ActivateGroup_Request) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[134]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ActivateGroup_Request.ProtoReflect.Descriptor instead.\nfunc (*ActivateGroup_Request) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{60, 0}\n}\n\nfunc (x *ActivateGroup_Request) GetGroupPk() []byte {\n\tif x != nil {\n\t\treturn x.GroupPk\n\t}\n\treturn nil\n}\n\nfunc (x *ActivateGroup_Request) GetLocalOnly() bool {\n\tif x != nil {\n\t\treturn x.LocalOnly\n\t}\n\treturn false\n}\n\ntype ActivateGroup_Reply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *ActivateGroup_Reply) Reset() {\n\t*x = ActivateGroup_Reply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[135]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ActivateGroup_Reply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ActivateGroup_Reply) ProtoMessage() {}\n\nfunc (x *ActivateGroup_Reply) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[135]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ActivateGroup_Reply.ProtoReflect.Descriptor instead.\nfunc (*ActivateGroup_Reply) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{60, 1}\n}\n\ntype DeactivateGroup_Request struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// group_pk is the identifier of the group\n\tGroupPk []byte `protobuf:\"bytes,1,opt,name=group_pk,json=groupPk,proto3\" json:\"group_pk,omitempty\"`\n}\n\nfunc (x *DeactivateGroup_Request) Reset() {\n\t*x = DeactivateGroup_Request{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[136]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *DeactivateGroup_Request) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*DeactivateGroup_Request) ProtoMessage() {}\n\nfunc (x *DeactivateGroup_Request) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[136]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use DeactivateGroup_Request.ProtoReflect.Descriptor instead.\nfunc (*DeactivateGroup_Request) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{61, 0}\n}\n\nfunc (x *DeactivateGroup_Request) GetGroupPk() []byte {\n\tif x != nil {\n\t\treturn x.GroupPk\n\t}\n\treturn nil\n}\n\ntype DeactivateGroup_Reply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *DeactivateGroup_Reply) Reset() {\n\t*x = DeactivateGroup_Reply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[137]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *DeactivateGroup_Reply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*DeactivateGroup_Reply) ProtoMessage() {}\n\nfunc (x *DeactivateGroup_Reply) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[137]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use DeactivateGroup_Reply.ProtoReflect.Descriptor instead.\nfunc (*DeactivateGroup_Reply) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{61, 1}\n}\n\ntype GroupDeviceStatus_Request struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tGroupPk []byte `protobuf:\"bytes,1,opt,name=group_pk,json=groupPk,proto3\" json:\"group_pk,omitempty\"`\n}\n\nfunc (x *GroupDeviceStatus_Request) Reset() {\n\t*x = GroupDeviceStatus_Request{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[138]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *GroupDeviceStatus_Request) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*GroupDeviceStatus_Request) ProtoMessage() {}\n\nfunc (x *GroupDeviceStatus_Request) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[138]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use GroupDeviceStatus_Request.ProtoReflect.Descriptor instead.\nfunc (*GroupDeviceStatus_Request) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{62, 0}\n}\n\nfunc (x *GroupDeviceStatus_Request) GetGroupPk() []byte {\n\tif x != nil {\n\t\treturn x.GroupPk\n\t}\n\treturn nil\n}\n\ntype GroupDeviceStatus_Reply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tType  GroupDeviceStatus_Type `protobuf:\"varint,1,opt,name=type,proto3,enum=weshnet.protocol.v1.GroupDeviceStatus_Type\" json:\"type,omitempty\"`\n\tEvent []byte                 `protobuf:\"bytes,2,opt,name=event,proto3\" json:\"event,omitempty\"`\n}\n\nfunc (x *GroupDeviceStatus_Reply) Reset() {\n\t*x = GroupDeviceStatus_Reply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[139]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *GroupDeviceStatus_Reply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*GroupDeviceStatus_Reply) ProtoMessage() {}\n\nfunc (x *GroupDeviceStatus_Reply) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[139]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use GroupDeviceStatus_Reply.ProtoReflect.Descriptor instead.\nfunc (*GroupDeviceStatus_Reply) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{62, 1}\n}\n\nfunc (x *GroupDeviceStatus_Reply) GetType() GroupDeviceStatus_Type {\n\tif x != nil {\n\t\treturn x.Type\n\t}\n\treturn GroupDeviceStatus_TypeUnknown\n}\n\nfunc (x *GroupDeviceStatus_Reply) GetEvent() []byte {\n\tif x != nil {\n\t\treturn x.Event\n\t}\n\treturn nil\n}\n\ntype GroupDeviceStatus_Reply_PeerConnected struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tPeerId     string                        `protobuf:\"bytes,1,opt,name=peer_id,json=peerId,proto3\" json:\"peer_id,omitempty\"`\n\tDevicePk   []byte                        `protobuf:\"bytes,2,opt,name=device_pk,json=devicePk,proto3\" json:\"device_pk,omitempty\"`\n\tTransports []GroupDeviceStatus_Transport `protobuf:\"varint,3,rep,packed,name=transports,proto3,enum=weshnet.protocol.v1.GroupDeviceStatus_Transport\" json:\"transports,omitempty\"`\n\tMaddrs     []string                      `protobuf:\"bytes,4,rep,name=maddrs,proto3\" json:\"maddrs,omitempty\"`\n}\n\nfunc (x *GroupDeviceStatus_Reply_PeerConnected) Reset() {\n\t*x = GroupDeviceStatus_Reply_PeerConnected{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[140]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *GroupDeviceStatus_Reply_PeerConnected) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*GroupDeviceStatus_Reply_PeerConnected) ProtoMessage() {}\n\nfunc (x *GroupDeviceStatus_Reply_PeerConnected) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[140]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use GroupDeviceStatus_Reply_PeerConnected.ProtoReflect.Descriptor instead.\nfunc (*GroupDeviceStatus_Reply_PeerConnected) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{62, 1, 0}\n}\n\nfunc (x *GroupDeviceStatus_Reply_PeerConnected) GetPeerId() string {\n\tif x != nil {\n\t\treturn x.PeerId\n\t}\n\treturn \"\"\n}\n\nfunc (x *GroupDeviceStatus_Reply_PeerConnected) GetDevicePk() []byte {\n\tif x != nil {\n\t\treturn x.DevicePk\n\t}\n\treturn nil\n}\n\nfunc (x *GroupDeviceStatus_Reply_PeerConnected) GetTransports() []GroupDeviceStatus_Transport {\n\tif x != nil {\n\t\treturn x.Transports\n\t}\n\treturn nil\n}\n\nfunc (x *GroupDeviceStatus_Reply_PeerConnected) GetMaddrs() []string {\n\tif x != nil {\n\t\treturn x.Maddrs\n\t}\n\treturn nil\n}\n\ntype GroupDeviceStatus_Reply_PeerReconnecting struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tPeerId string `protobuf:\"bytes,1,opt,name=peer_id,json=peerId,proto3\" json:\"peer_id,omitempty\"`\n}\n\nfunc (x *GroupDeviceStatus_Reply_PeerReconnecting) Reset() {\n\t*x = GroupDeviceStatus_Reply_PeerReconnecting{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[141]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *GroupDeviceStatus_Reply_PeerReconnecting) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*GroupDeviceStatus_Reply_PeerReconnecting) ProtoMessage() {}\n\nfunc (x *GroupDeviceStatus_Reply_PeerReconnecting) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[141]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use GroupDeviceStatus_Reply_PeerReconnecting.ProtoReflect.Descriptor instead.\nfunc (*GroupDeviceStatus_Reply_PeerReconnecting) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{62, 1, 1}\n}\n\nfunc (x *GroupDeviceStatus_Reply_PeerReconnecting) GetPeerId() string {\n\tif x != nil {\n\t\treturn x.PeerId\n\t}\n\treturn \"\"\n}\n\ntype GroupDeviceStatus_Reply_PeerDisconnected struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tPeerId string `protobuf:\"bytes,1,opt,name=peer_id,json=peerId,proto3\" json:\"peer_id,omitempty\"`\n}\n\nfunc (x *GroupDeviceStatus_Reply_PeerDisconnected) Reset() {\n\t*x = GroupDeviceStatus_Reply_PeerDisconnected{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[142]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *GroupDeviceStatus_Reply_PeerDisconnected) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*GroupDeviceStatus_Reply_PeerDisconnected) ProtoMessage() {}\n\nfunc (x *GroupDeviceStatus_Reply_PeerDisconnected) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[142]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use GroupDeviceStatus_Reply_PeerDisconnected.ProtoReflect.Descriptor instead.\nfunc (*GroupDeviceStatus_Reply_PeerDisconnected) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{62, 1, 2}\n}\n\nfunc (x *GroupDeviceStatus_Reply_PeerDisconnected) GetPeerId() string {\n\tif x != nil {\n\t\treturn x.PeerId\n\t}\n\treturn \"\"\n}\n\ntype DebugListGroups_Request struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *DebugListGroups_Request) Reset() {\n\t*x = DebugListGroups_Request{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[143]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *DebugListGroups_Request) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*DebugListGroups_Request) ProtoMessage() {}\n\nfunc (x *DebugListGroups_Request) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[143]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use DebugListGroups_Request.ProtoReflect.Descriptor instead.\nfunc (*DebugListGroups_Request) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{63, 0}\n}\n\ntype DebugListGroups_Reply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// group_pk is the public key of the group\n\tGroupPk []byte `protobuf:\"bytes,1,opt,name=group_pk,json=groupPk,proto3\" json:\"group_pk,omitempty\"`\n\t// group_type is the type of the group\n\tGroupType GroupType `protobuf:\"varint,2,opt,name=group_type,json=groupType,proto3,enum=weshnet.protocol.v1.GroupType\" json:\"group_type,omitempty\"`\n\t// contact_pk is the contact public key if appropriate\n\tContactPk []byte `protobuf:\"bytes,3,opt,name=contact_pk,json=contactPk,proto3\" json:\"contact_pk,omitempty\"`\n}\n\nfunc (x *DebugListGroups_Reply) Reset() {\n\t*x = DebugListGroups_Reply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[144]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *DebugListGroups_Reply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*DebugListGroups_Reply) ProtoMessage() {}\n\nfunc (x *DebugListGroups_Reply) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[144]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use DebugListGroups_Reply.ProtoReflect.Descriptor instead.\nfunc (*DebugListGroups_Reply) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{63, 1}\n}\n\nfunc (x *DebugListGroups_Reply) GetGroupPk() []byte {\n\tif x != nil {\n\t\treturn x.GroupPk\n\t}\n\treturn nil\n}\n\nfunc (x *DebugListGroups_Reply) GetGroupType() GroupType {\n\tif x != nil {\n\t\treturn x.GroupType\n\t}\n\treturn GroupType_GroupTypeUndefined\n}\n\nfunc (x *DebugListGroups_Reply) GetContactPk() []byte {\n\tif x != nil {\n\t\treturn x.ContactPk\n\t}\n\treturn nil\n}\n\ntype DebugInspectGroupStore_Request struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// group_pk is the identifier of the group\n\tGroupPk []byte `protobuf:\"bytes,1,opt,name=group_pk,json=groupPk,proto3\" json:\"group_pk,omitempty\"`\n\t// log_type is the log to inspect\n\tLogType DebugInspectGroupLogType `protobuf:\"varint,2,opt,name=log_type,json=logType,proto3,enum=weshnet.protocol.v1.DebugInspectGroupLogType\" json:\"log_type,omitempty\"`\n}\n\nfunc (x *DebugInspectGroupStore_Request) Reset() {\n\t*x = DebugInspectGroupStore_Request{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[145]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *DebugInspectGroupStore_Request) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*DebugInspectGroupStore_Request) ProtoMessage() {}\n\nfunc (x *DebugInspectGroupStore_Request) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[145]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use DebugInspectGroupStore_Request.ProtoReflect.Descriptor instead.\nfunc (*DebugInspectGroupStore_Request) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{64, 0}\n}\n\nfunc (x *DebugInspectGroupStore_Request) GetGroupPk() []byte {\n\tif x != nil {\n\t\treturn x.GroupPk\n\t}\n\treturn nil\n}\n\nfunc (x *DebugInspectGroupStore_Request) GetLogType() DebugInspectGroupLogType {\n\tif x != nil {\n\t\treturn x.LogType\n\t}\n\treturn DebugInspectGroupLogType_DebugInspectGroupLogTypeUndefined\n}\n\ntype DebugInspectGroupStore_Reply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// cid is the CID of the IPFS log entry\n\tCid []byte `protobuf:\"bytes,1,opt,name=cid,proto3\" json:\"cid,omitempty\"`\n\t// parent_cids is the list of the parent entries\n\tParentCids [][]byte `protobuf:\"bytes,2,rep,name=parent_cids,json=parentCids,proto3\" json:\"parent_cids,omitempty\"`\n\t// event_type metadata event type if subscribed to metadata events\n\tMetadataEventType EventType `protobuf:\"varint,3,opt,name=metadata_event_type,json=metadataEventType,proto3,enum=weshnet.protocol.v1.EventType\" json:\"metadata_event_type,omitempty\"`\n\t// device_pk is the public key of the device signing the entry\n\tDevicePk []byte `protobuf:\"bytes,4,opt,name=device_pk,json=devicePk,proto3\" json:\"device_pk,omitempty\"`\n\t// payload is the un encrypted entry payload if available\n\tPayload []byte `protobuf:\"bytes,6,opt,name=payload,proto3\" json:\"payload,omitempty\"`\n}\n\nfunc (x *DebugInspectGroupStore_Reply) Reset() {\n\t*x = DebugInspectGroupStore_Reply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[146]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *DebugInspectGroupStore_Reply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*DebugInspectGroupStore_Reply) ProtoMessage() {}\n\nfunc (x *DebugInspectGroupStore_Reply) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[146]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use DebugInspectGroupStore_Reply.ProtoReflect.Descriptor instead.\nfunc (*DebugInspectGroupStore_Reply) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{64, 1}\n}\n\nfunc (x *DebugInspectGroupStore_Reply) GetCid() []byte {\n\tif x != nil {\n\t\treturn x.Cid\n\t}\n\treturn nil\n}\n\nfunc (x *DebugInspectGroupStore_Reply) GetParentCids() [][]byte {\n\tif x != nil {\n\t\treturn x.ParentCids\n\t}\n\treturn nil\n}\n\nfunc (x *DebugInspectGroupStore_Reply) GetMetadataEventType() EventType {\n\tif x != nil {\n\t\treturn x.MetadataEventType\n\t}\n\treturn EventType_EventTypeUndefined\n}\n\nfunc (x *DebugInspectGroupStore_Reply) GetDevicePk() []byte {\n\tif x != nil {\n\t\treturn x.DevicePk\n\t}\n\treturn nil\n}\n\nfunc (x *DebugInspectGroupStore_Reply) GetPayload() []byte {\n\tif x != nil {\n\t\treturn x.Payload\n\t}\n\treturn nil\n}\n\ntype DebugGroup_Request struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// group_pk is the identifier of the group\n\tGroupPk []byte `protobuf:\"bytes,1,opt,name=group_pk,json=groupPk,proto3\" json:\"group_pk,omitempty\"`\n}\n\nfunc (x *DebugGroup_Request) Reset() {\n\t*x = DebugGroup_Request{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[147]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *DebugGroup_Request) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*DebugGroup_Request) ProtoMessage() {}\n\nfunc (x *DebugGroup_Request) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[147]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use DebugGroup_Request.ProtoReflect.Descriptor instead.\nfunc (*DebugGroup_Request) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{65, 0}\n}\n\nfunc (x *DebugGroup_Request) GetGroupPk() []byte {\n\tif x != nil {\n\t\treturn x.GroupPk\n\t}\n\treturn nil\n}\n\ntype DebugGroup_Reply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// peer_ids is the list of peer ids connected to the same group\n\tPeerIds []string `protobuf:\"bytes,1,rep,name=peer_ids,json=peerIds,proto3\" json:\"peer_ids,omitempty\"`\n}\n\nfunc (x *DebugGroup_Reply) Reset() {\n\t*x = DebugGroup_Reply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[148]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *DebugGroup_Reply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*DebugGroup_Reply) ProtoMessage() {}\n\nfunc (x *DebugGroup_Reply) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[148]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use DebugGroup_Reply.ProtoReflect.Descriptor instead.\nfunc (*DebugGroup_Reply) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{65, 1}\n}\n\nfunc (x *DebugGroup_Reply) GetPeerIds() []string {\n\tif x != nil {\n\t\treturn x.PeerIds\n\t}\n\treturn nil\n}\n\ntype CredentialVerificationServiceInitFlow_Request struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tServiceUrl string `protobuf:\"bytes,1,opt,name=service_url,json=serviceUrl,proto3\" json:\"service_url,omitempty\"`\n\tPublicKey  []byte `protobuf:\"bytes,2,opt,name=public_key,json=publicKey,proto3\" json:\"public_key,omitempty\"`\n\tLink       string `protobuf:\"bytes,3,opt,name=link,proto3\" json:\"link,omitempty\"`\n}\n\nfunc (x *CredentialVerificationServiceInitFlow_Request) Reset() {\n\t*x = CredentialVerificationServiceInitFlow_Request{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[149]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *CredentialVerificationServiceInitFlow_Request) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*CredentialVerificationServiceInitFlow_Request) ProtoMessage() {}\n\nfunc (x *CredentialVerificationServiceInitFlow_Request) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[149]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use CredentialVerificationServiceInitFlow_Request.ProtoReflect.Descriptor instead.\nfunc (*CredentialVerificationServiceInitFlow_Request) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{69, 0}\n}\n\nfunc (x *CredentialVerificationServiceInitFlow_Request) GetServiceUrl() string {\n\tif x != nil {\n\t\treturn x.ServiceUrl\n\t}\n\treturn \"\"\n}\n\nfunc (x *CredentialVerificationServiceInitFlow_Request) GetPublicKey() []byte {\n\tif x != nil {\n\t\treturn x.PublicKey\n\t}\n\treturn nil\n}\n\nfunc (x *CredentialVerificationServiceInitFlow_Request) GetLink() string {\n\tif x != nil {\n\t\treturn x.Link\n\t}\n\treturn \"\"\n}\n\ntype CredentialVerificationServiceInitFlow_Reply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tUrl       string `protobuf:\"bytes,1,opt,name=url,proto3\" json:\"url,omitempty\"`\n\tSecureUrl bool   `protobuf:\"varint,2,opt,name=secure_url,json=secureUrl,proto3\" json:\"secure_url,omitempty\"`\n}\n\nfunc (x *CredentialVerificationServiceInitFlow_Reply) Reset() {\n\t*x = CredentialVerificationServiceInitFlow_Reply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[150]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *CredentialVerificationServiceInitFlow_Reply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*CredentialVerificationServiceInitFlow_Reply) ProtoMessage() {}\n\nfunc (x *CredentialVerificationServiceInitFlow_Reply) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[150]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use CredentialVerificationServiceInitFlow_Reply.ProtoReflect.Descriptor instead.\nfunc (*CredentialVerificationServiceInitFlow_Reply) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{69, 1}\n}\n\nfunc (x *CredentialVerificationServiceInitFlow_Reply) GetUrl() string {\n\tif x != nil {\n\t\treturn x.Url\n\t}\n\treturn \"\"\n}\n\nfunc (x *CredentialVerificationServiceInitFlow_Reply) GetSecureUrl() bool {\n\tif x != nil {\n\t\treturn x.SecureUrl\n\t}\n\treturn false\n}\n\ntype CredentialVerificationServiceCompleteFlow_Request struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tCallbackUri string `protobuf:\"bytes,1,opt,name=callback_uri,json=callbackUri,proto3\" json:\"callback_uri,omitempty\"`\n}\n\nfunc (x *CredentialVerificationServiceCompleteFlow_Request) Reset() {\n\t*x = CredentialVerificationServiceCompleteFlow_Request{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[151]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *CredentialVerificationServiceCompleteFlow_Request) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*CredentialVerificationServiceCompleteFlow_Request) ProtoMessage() {}\n\nfunc (x *CredentialVerificationServiceCompleteFlow_Request) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[151]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use CredentialVerificationServiceCompleteFlow_Request.ProtoReflect.Descriptor instead.\nfunc (*CredentialVerificationServiceCompleteFlow_Request) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{70, 0}\n}\n\nfunc (x *CredentialVerificationServiceCompleteFlow_Request) GetCallbackUri() string {\n\tif x != nil {\n\t\treturn x.CallbackUri\n\t}\n\treturn \"\"\n}\n\ntype CredentialVerificationServiceCompleteFlow_Reply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tIdentifier string `protobuf:\"bytes,1,opt,name=identifier,proto3\" json:\"identifier,omitempty\"`\n}\n\nfunc (x *CredentialVerificationServiceCompleteFlow_Reply) Reset() {\n\t*x = CredentialVerificationServiceCompleteFlow_Reply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[152]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *CredentialVerificationServiceCompleteFlow_Reply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*CredentialVerificationServiceCompleteFlow_Reply) ProtoMessage() {}\n\nfunc (x *CredentialVerificationServiceCompleteFlow_Reply) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[152]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use CredentialVerificationServiceCompleteFlow_Reply.ProtoReflect.Descriptor instead.\nfunc (*CredentialVerificationServiceCompleteFlow_Reply) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{70, 1}\n}\n\nfunc (x *CredentialVerificationServiceCompleteFlow_Reply) GetIdentifier() string {\n\tif x != nil {\n\t\treturn x.Identifier\n\t}\n\treturn \"\"\n}\n\ntype VerifiedCredentialsList_Request struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tFilterIdentifier string `protobuf:\"bytes,1,opt,name=filter_identifier,json=filterIdentifier,proto3\" json:\"filter_identifier,omitempty\"`\n\tFilterIssuer     string `protobuf:\"bytes,2,opt,name=filter_issuer,json=filterIssuer,proto3\" json:\"filter_issuer,omitempty\"`\n\tExcludeExpired   bool   `protobuf:\"varint,3,opt,name=exclude_expired,json=excludeExpired,proto3\" json:\"exclude_expired,omitempty\"`\n}\n\nfunc (x *VerifiedCredentialsList_Request) Reset() {\n\t*x = VerifiedCredentialsList_Request{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[153]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *VerifiedCredentialsList_Request) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*VerifiedCredentialsList_Request) ProtoMessage() {}\n\nfunc (x *VerifiedCredentialsList_Request) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[153]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use VerifiedCredentialsList_Request.ProtoReflect.Descriptor instead.\nfunc (*VerifiedCredentialsList_Request) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{71, 0}\n}\n\nfunc (x *VerifiedCredentialsList_Request) GetFilterIdentifier() string {\n\tif x != nil {\n\t\treturn x.FilterIdentifier\n\t}\n\treturn \"\"\n}\n\nfunc (x *VerifiedCredentialsList_Request) GetFilterIssuer() string {\n\tif x != nil {\n\t\treturn x.FilterIssuer\n\t}\n\treturn \"\"\n}\n\nfunc (x *VerifiedCredentialsList_Request) GetExcludeExpired() bool {\n\tif x != nil {\n\t\treturn x.ExcludeExpired\n\t}\n\treturn false\n}\n\ntype VerifiedCredentialsList_Reply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tCredential *AccountVerifiedCredentialRegistered `protobuf:\"bytes,1,opt,name=credential,proto3\" json:\"credential,omitempty\"`\n}\n\nfunc (x *VerifiedCredentialsList_Reply) Reset() {\n\t*x = VerifiedCredentialsList_Reply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[154]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *VerifiedCredentialsList_Reply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*VerifiedCredentialsList_Reply) ProtoMessage() {}\n\nfunc (x *VerifiedCredentialsList_Reply) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[154]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use VerifiedCredentialsList_Reply.ProtoReflect.Descriptor instead.\nfunc (*VerifiedCredentialsList_Reply) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{71, 1}\n}\n\nfunc (x *VerifiedCredentialsList_Reply) GetCredential() *AccountVerifiedCredentialRegistered {\n\tif x != nil {\n\t\treturn x.Credential\n\t}\n\treturn nil\n}\n\ntype ReplicationServiceRegisterGroup_Request struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tGroupPk           []byte `protobuf:\"bytes,1,opt,name=group_pk,json=groupPk,proto3\" json:\"group_pk,omitempty\"`\n\tToken             string `protobuf:\"bytes,2,opt,name=token,proto3\" json:\"token,omitempty\"`\n\tAuthenticationUrl string `protobuf:\"bytes,3,opt,name=authentication_url,json=authenticationUrl,proto3\" json:\"authentication_url,omitempty\"`\n\tReplicationServer string `protobuf:\"bytes,4,opt,name=replication_server,json=replicationServer,proto3\" json:\"replication_server,omitempty\"`\n}\n\nfunc (x *ReplicationServiceRegisterGroup_Request) Reset() {\n\t*x = ReplicationServiceRegisterGroup_Request{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[155]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ReplicationServiceRegisterGroup_Request) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ReplicationServiceRegisterGroup_Request) ProtoMessage() {}\n\nfunc (x *ReplicationServiceRegisterGroup_Request) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[155]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ReplicationServiceRegisterGroup_Request.ProtoReflect.Descriptor instead.\nfunc (*ReplicationServiceRegisterGroup_Request) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{72, 0}\n}\n\nfunc (x *ReplicationServiceRegisterGroup_Request) GetGroupPk() []byte {\n\tif x != nil {\n\t\treturn x.GroupPk\n\t}\n\treturn nil\n}\n\nfunc (x *ReplicationServiceRegisterGroup_Request) GetToken() string {\n\tif x != nil {\n\t\treturn x.Token\n\t}\n\treturn \"\"\n}\n\nfunc (x *ReplicationServiceRegisterGroup_Request) GetAuthenticationUrl() string {\n\tif x != nil {\n\t\treturn x.AuthenticationUrl\n\t}\n\treturn \"\"\n}\n\nfunc (x *ReplicationServiceRegisterGroup_Request) GetReplicationServer() string {\n\tif x != nil {\n\t\treturn x.ReplicationServer\n\t}\n\treturn \"\"\n}\n\ntype ReplicationServiceRegisterGroup_Reply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *ReplicationServiceRegisterGroup_Reply) Reset() {\n\t*x = ReplicationServiceRegisterGroup_Reply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[156]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ReplicationServiceRegisterGroup_Reply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ReplicationServiceRegisterGroup_Reply) ProtoMessage() {}\n\nfunc (x *ReplicationServiceRegisterGroup_Reply) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[156]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ReplicationServiceRegisterGroup_Reply.ProtoReflect.Descriptor instead.\nfunc (*ReplicationServiceRegisterGroup_Reply) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{72, 1}\n}\n\ntype ReplicationServiceReplicateGroup_Request struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tGroup *Group `protobuf:\"bytes,1,opt,name=group,proto3\" json:\"group,omitempty\"`\n}\n\nfunc (x *ReplicationServiceReplicateGroup_Request) Reset() {\n\t*x = ReplicationServiceReplicateGroup_Request{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[157]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ReplicationServiceReplicateGroup_Request) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ReplicationServiceReplicateGroup_Request) ProtoMessage() {}\n\nfunc (x *ReplicationServiceReplicateGroup_Request) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[157]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ReplicationServiceReplicateGroup_Request.ProtoReflect.Descriptor instead.\nfunc (*ReplicationServiceReplicateGroup_Request) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{73, 0}\n}\n\nfunc (x *ReplicationServiceReplicateGroup_Request) GetGroup() *Group {\n\tif x != nil {\n\t\treturn x.Group\n\t}\n\treturn nil\n}\n\ntype ReplicationServiceReplicateGroup_Reply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tOk bool `protobuf:\"varint,1,opt,name=ok,proto3\" json:\"ok,omitempty\"`\n}\n\nfunc (x *ReplicationServiceReplicateGroup_Reply) Reset() {\n\t*x = ReplicationServiceReplicateGroup_Reply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[158]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ReplicationServiceReplicateGroup_Reply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ReplicationServiceReplicateGroup_Reply) ProtoMessage() {}\n\nfunc (x *ReplicationServiceReplicateGroup_Reply) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[158]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ReplicationServiceReplicateGroup_Reply.ProtoReflect.Descriptor instead.\nfunc (*ReplicationServiceReplicateGroup_Reply) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{73, 1}\n}\n\nfunc (x *ReplicationServiceReplicateGroup_Reply) GetOk() bool {\n\tif x != nil {\n\t\treturn x.Ok\n\t}\n\treturn false\n}\n\ntype SystemInfo_Request struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *SystemInfo_Request) Reset() {\n\t*x = SystemInfo_Request{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[159]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *SystemInfo_Request) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*SystemInfo_Request) ProtoMessage() {}\n\nfunc (x *SystemInfo_Request) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[159]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use SystemInfo_Request.ProtoReflect.Descriptor instead.\nfunc (*SystemInfo_Request) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{74, 0}\n}\n\ntype SystemInfo_Reply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tProcess *SystemInfo_Process `protobuf:\"bytes,1,opt,name=process,proto3\" json:\"process,omitempty\"`\n\tP2P     *SystemInfo_P2P     `protobuf:\"bytes,2,opt,name=p2p,proto3\" json:\"p2p,omitempty\"`\n\tOrbitdb *SystemInfo_OrbitDB `protobuf:\"bytes,3,opt,name=orbitdb,proto3\" json:\"orbitdb,omitempty\"`\n\tWarns   []string            `protobuf:\"bytes,4,rep,name=warns,proto3\" json:\"warns,omitempty\"`\n}\n\nfunc (x *SystemInfo_Reply) Reset() {\n\t*x = SystemInfo_Reply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[160]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *SystemInfo_Reply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*SystemInfo_Reply) ProtoMessage() {}\n\nfunc (x *SystemInfo_Reply) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[160]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use SystemInfo_Reply.ProtoReflect.Descriptor instead.\nfunc (*SystemInfo_Reply) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{74, 1}\n}\n\nfunc (x *SystemInfo_Reply) GetProcess() *SystemInfo_Process {\n\tif x != nil {\n\t\treturn x.Process\n\t}\n\treturn nil\n}\n\nfunc (x *SystemInfo_Reply) GetP2P() *SystemInfo_P2P {\n\tif x != nil {\n\t\treturn x.P2P\n\t}\n\treturn nil\n}\n\nfunc (x *SystemInfo_Reply) GetOrbitdb() *SystemInfo_OrbitDB {\n\tif x != nil {\n\t\treturn x.Orbitdb\n\t}\n\treturn nil\n}\n\nfunc (x *SystemInfo_Reply) GetWarns() []string {\n\tif x != nil {\n\t\treturn x.Warns\n\t}\n\treturn nil\n}\n\ntype SystemInfo_OrbitDB struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tAccountMetadata *SystemInfo_OrbitDB_ReplicationStatus `protobuf:\"bytes,1,opt,name=account_metadata,json=accountMetadata,proto3\" json:\"account_metadata,omitempty\"`\n}\n\nfunc (x *SystemInfo_OrbitDB) Reset() {\n\t*x = SystemInfo_OrbitDB{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[161]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *SystemInfo_OrbitDB) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*SystemInfo_OrbitDB) ProtoMessage() {}\n\nfunc (x *SystemInfo_OrbitDB) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[161]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use SystemInfo_OrbitDB.ProtoReflect.Descriptor instead.\nfunc (*SystemInfo_OrbitDB) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{74, 2}\n}\n\nfunc (x *SystemInfo_OrbitDB) GetAccountMetadata() *SystemInfo_OrbitDB_ReplicationStatus {\n\tif x != nil {\n\t\treturn x.AccountMetadata\n\t}\n\treturn nil\n}\n\ntype SystemInfo_P2P struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tConnectedPeers int64 `protobuf:\"varint,1,opt,name=connected_peers,json=connectedPeers,proto3\" json:\"connected_peers,omitempty\"`\n}\n\nfunc (x *SystemInfo_P2P) Reset() {\n\t*x = SystemInfo_P2P{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[162]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *SystemInfo_P2P) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*SystemInfo_P2P) ProtoMessage() {}\n\nfunc (x *SystemInfo_P2P) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[162]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use SystemInfo_P2P.ProtoReflect.Descriptor instead.\nfunc (*SystemInfo_P2P) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{74, 3}\n}\n\nfunc (x *SystemInfo_P2P) GetConnectedPeers() int64 {\n\tif x != nil {\n\t\treturn x.ConnectedPeers\n\t}\n\treturn 0\n}\n\ntype SystemInfo_Process struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tVersion          string `protobuf:\"bytes,1,opt,name=version,proto3\" json:\"version,omitempty\"`\n\tVcsRef           string `protobuf:\"bytes,2,opt,name=vcs_ref,json=vcsRef,proto3\" json:\"vcs_ref,omitempty\"`\n\tUptimeMs         int64  `protobuf:\"varint,3,opt,name=uptime_ms,json=uptimeMs,proto3\" json:\"uptime_ms,omitempty\"`\n\tUserCpuTimeMs    int64  `protobuf:\"varint,10,opt,name=user_cpu_time_ms,json=userCpuTimeMs,proto3\" json:\"user_cpu_time_ms,omitempty\"`\n\tSystemCpuTimeMs  int64  `protobuf:\"varint,11,opt,name=system_cpu_time_ms,json=systemCpuTimeMs,proto3\" json:\"system_cpu_time_ms,omitempty\"`\n\tStartedAt        int64  `protobuf:\"varint,12,opt,name=started_at,json=startedAt,proto3\" json:\"started_at,omitempty\"`\n\tRlimitCur        uint64 `protobuf:\"varint,13,opt,name=rlimit_cur,json=rlimitCur,proto3\" json:\"rlimit_cur,omitempty\"`\n\tNumGoroutine     int64  `protobuf:\"varint,14,opt,name=num_goroutine,json=numGoroutine,proto3\" json:\"num_goroutine,omitempty\"`\n\tNofile           int64  `protobuf:\"varint,15,opt,name=nofile,proto3\" json:\"nofile,omitempty\"`\n\tTooManyOpenFiles bool   `protobuf:\"varint,16,opt,name=too_many_open_files,json=tooManyOpenFiles,proto3\" json:\"too_many_open_files,omitempty\"`\n\tNumCpu           int64  `protobuf:\"varint,17,opt,name=num_cpu,json=numCpu,proto3\" json:\"num_cpu,omitempty\"`\n\tGoVersion        string `protobuf:\"bytes,18,opt,name=go_version,json=goVersion,proto3\" json:\"go_version,omitempty\"`\n\tOperatingSystem  string `protobuf:\"bytes,19,opt,name=operating_system,json=operatingSystem,proto3\" json:\"operating_system,omitempty\"`\n\tHostName         string `protobuf:\"bytes,20,opt,name=host_name,json=hostName,proto3\" json:\"host_name,omitempty\"`\n\tArch             string `protobuf:\"bytes,21,opt,name=arch,proto3\" json:\"arch,omitempty\"`\n\tRlimitMax        uint64 `protobuf:\"varint,22,opt,name=rlimit_max,json=rlimitMax,proto3\" json:\"rlimit_max,omitempty\"`\n\tPid              int64  `protobuf:\"varint,23,opt,name=pid,proto3\" json:\"pid,omitempty\"`\n\tPpid             int64  `protobuf:\"varint,24,opt,name=ppid,proto3\" json:\"ppid,omitempty\"`\n\tPriority         int64  `protobuf:\"varint,25,opt,name=priority,proto3\" json:\"priority,omitempty\"`\n\tUid              int64  `protobuf:\"varint,26,opt,name=uid,proto3\" json:\"uid,omitempty\"`\n\tWorkingDir       string `protobuf:\"bytes,27,opt,name=working_dir,json=workingDir,proto3\" json:\"working_dir,omitempty\"`\n\tSystemUsername   string `protobuf:\"bytes,28,opt,name=system_username,json=systemUsername,proto3\" json:\"system_username,omitempty\"`\n}\n\nfunc (x *SystemInfo_Process) Reset() {\n\t*x = SystemInfo_Process{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[163]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *SystemInfo_Process) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*SystemInfo_Process) ProtoMessage() {}\n\nfunc (x *SystemInfo_Process) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[163]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use SystemInfo_Process.ProtoReflect.Descriptor instead.\nfunc (*SystemInfo_Process) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{74, 4}\n}\n\nfunc (x *SystemInfo_Process) GetVersion() string {\n\tif x != nil {\n\t\treturn x.Version\n\t}\n\treturn \"\"\n}\n\nfunc (x *SystemInfo_Process) GetVcsRef() string {\n\tif x != nil {\n\t\treturn x.VcsRef\n\t}\n\treturn \"\"\n}\n\nfunc (x *SystemInfo_Process) GetUptimeMs() int64 {\n\tif x != nil {\n\t\treturn x.UptimeMs\n\t}\n\treturn 0\n}\n\nfunc (x *SystemInfo_Process) GetUserCpuTimeMs() int64 {\n\tif x != nil {\n\t\treturn x.UserCpuTimeMs\n\t}\n\treturn 0\n}\n\nfunc (x *SystemInfo_Process) GetSystemCpuTimeMs() int64 {\n\tif x != nil {\n\t\treturn x.SystemCpuTimeMs\n\t}\n\treturn 0\n}\n\nfunc (x *SystemInfo_Process) GetStartedAt() int64 {\n\tif x != nil {\n\t\treturn x.StartedAt\n\t}\n\treturn 0\n}\n\nfunc (x *SystemInfo_Process) GetRlimitCur() uint64 {\n\tif x != nil {\n\t\treturn x.RlimitCur\n\t}\n\treturn 0\n}\n\nfunc (x *SystemInfo_Process) GetNumGoroutine() int64 {\n\tif x != nil {\n\t\treturn x.NumGoroutine\n\t}\n\treturn 0\n}\n\nfunc (x *SystemInfo_Process) GetNofile() int64 {\n\tif x != nil {\n\t\treturn x.Nofile\n\t}\n\treturn 0\n}\n\nfunc (x *SystemInfo_Process) GetTooManyOpenFiles() bool {\n\tif x != nil {\n\t\treturn x.TooManyOpenFiles\n\t}\n\treturn false\n}\n\nfunc (x *SystemInfo_Process) GetNumCpu() int64 {\n\tif x != nil {\n\t\treturn x.NumCpu\n\t}\n\treturn 0\n}\n\nfunc (x *SystemInfo_Process) GetGoVersion() string {\n\tif x != nil {\n\t\treturn x.GoVersion\n\t}\n\treturn \"\"\n}\n\nfunc (x *SystemInfo_Process) GetOperatingSystem() string {\n\tif x != nil {\n\t\treturn x.OperatingSystem\n\t}\n\treturn \"\"\n}\n\nfunc (x *SystemInfo_Process) GetHostName() string {\n\tif x != nil {\n\t\treturn x.HostName\n\t}\n\treturn \"\"\n}\n\nfunc (x *SystemInfo_Process) GetArch() string {\n\tif x != nil {\n\t\treturn x.Arch\n\t}\n\treturn \"\"\n}\n\nfunc (x *SystemInfo_Process) GetRlimitMax() uint64 {\n\tif x != nil {\n\t\treturn x.RlimitMax\n\t}\n\treturn 0\n}\n\nfunc (x *SystemInfo_Process) GetPid() int64 {\n\tif x != nil {\n\t\treturn x.Pid\n\t}\n\treturn 0\n}\n\nfunc (x *SystemInfo_Process) GetPpid() int64 {\n\tif x != nil {\n\t\treturn x.Ppid\n\t}\n\treturn 0\n}\n\nfunc (x *SystemInfo_Process) GetPriority() int64 {\n\tif x != nil {\n\t\treturn x.Priority\n\t}\n\treturn 0\n}\n\nfunc (x *SystemInfo_Process) GetUid() int64 {\n\tif x != nil {\n\t\treturn x.Uid\n\t}\n\treturn 0\n}\n\nfunc (x *SystemInfo_Process) GetWorkingDir() string {\n\tif x != nil {\n\t\treturn x.WorkingDir\n\t}\n\treturn \"\"\n}\n\nfunc (x *SystemInfo_Process) GetSystemUsername() string {\n\tif x != nil {\n\t\treturn x.SystemUsername\n\t}\n\treturn \"\"\n}\n\ntype SystemInfo_OrbitDB_ReplicationStatus struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tProgress int64 `protobuf:\"varint,1,opt,name=progress,proto3\" json:\"progress,omitempty\"`\n\tMaximum  int64 `protobuf:\"varint,2,opt,name=maximum,proto3\" json:\"maximum,omitempty\"`\n\tBuffered int64 `protobuf:\"varint,3,opt,name=buffered,proto3\" json:\"buffered,omitempty\"`\n\tQueued   int64 `protobuf:\"varint,4,opt,name=queued,proto3\" json:\"queued,omitempty\"`\n}\n\nfunc (x *SystemInfo_OrbitDB_ReplicationStatus) Reset() {\n\t*x = SystemInfo_OrbitDB_ReplicationStatus{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[164]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *SystemInfo_OrbitDB_ReplicationStatus) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*SystemInfo_OrbitDB_ReplicationStatus) ProtoMessage() {}\n\nfunc (x *SystemInfo_OrbitDB_ReplicationStatus) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[164]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use SystemInfo_OrbitDB_ReplicationStatus.ProtoReflect.Descriptor instead.\nfunc (*SystemInfo_OrbitDB_ReplicationStatus) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{74, 2, 0}\n}\n\nfunc (x *SystemInfo_OrbitDB_ReplicationStatus) GetProgress() int64 {\n\tif x != nil {\n\t\treturn x.Progress\n\t}\n\treturn 0\n}\n\nfunc (x *SystemInfo_OrbitDB_ReplicationStatus) GetMaximum() int64 {\n\tif x != nil {\n\t\treturn x.Maximum\n\t}\n\treturn 0\n}\n\nfunc (x *SystemInfo_OrbitDB_ReplicationStatus) GetBuffered() int64 {\n\tif x != nil {\n\t\treturn x.Buffered\n\t}\n\treturn 0\n}\n\nfunc (x *SystemInfo_OrbitDB_ReplicationStatus) GetQueued() int64 {\n\tif x != nil {\n\t\treturn x.Queued\n\t}\n\treturn 0\n}\n\ntype PeerList_Request struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *PeerList_Request) Reset() {\n\t*x = PeerList_Request{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[165]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *PeerList_Request) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*PeerList_Request) ProtoMessage() {}\n\nfunc (x *PeerList_Request) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[165]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use PeerList_Request.ProtoReflect.Descriptor instead.\nfunc (*PeerList_Request) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{75, 0}\n}\n\ntype PeerList_Reply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tPeers []*PeerList_Peer `protobuf:\"bytes,1,rep,name=peers,proto3\" json:\"peers,omitempty\"`\n}\n\nfunc (x *PeerList_Reply) Reset() {\n\t*x = PeerList_Reply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[166]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *PeerList_Reply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*PeerList_Reply) ProtoMessage() {}\n\nfunc (x *PeerList_Reply) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[166]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use PeerList_Reply.ProtoReflect.Descriptor instead.\nfunc (*PeerList_Reply) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{75, 1}\n}\n\nfunc (x *PeerList_Reply) GetPeers() []*PeerList_Peer {\n\tif x != nil {\n\t\treturn x.Peers\n\t}\n\treturn nil\n}\n\ntype PeerList_Peer struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// id is the libp2p.PeerID.\n\tId string `protobuf:\"bytes,1,opt,name=id,proto3\" json:\"id,omitempty\"`\n\t// routes are the list of active and known maddr.\n\tRoutes []*PeerList_Route `protobuf:\"bytes,2,rep,name=routes,proto3\" json:\"routes,omitempty\"`\n\t// errors is a list of errors related to the peer.\n\tErrors []string `protobuf:\"bytes,3,rep,name=errors,proto3\" json:\"errors,omitempty\"`\n\t// Features is a list of available features.\n\tFeatures []PeerList_Feature `protobuf:\"varint,4,rep,packed,name=features,proto3,enum=weshnet.protocol.v1.PeerList_Feature\" json:\"features,omitempty\"`\n\t// MinLatency is the minimum latency across all the peer routes.\n\tMinLatency int64 `protobuf:\"varint,5,opt,name=min_latency,json=minLatency,proto3\" json:\"min_latency,omitempty\"`\n\t// IsActive is true if at least one of the route is active.\n\tIsActive bool `protobuf:\"varint,6,opt,name=is_active,json=isActive,proto3\" json:\"is_active,omitempty\"`\n\t// Direction is the aggregate of all the routes's direction.\n\tDirection Direction `protobuf:\"varint,7,opt,name=direction,proto3,enum=weshnet.protocol.v1.Direction\" json:\"direction,omitempty\"`\n}\n\nfunc (x *PeerList_Peer) Reset() {\n\t*x = PeerList_Peer{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[167]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *PeerList_Peer) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*PeerList_Peer) ProtoMessage() {}\n\nfunc (x *PeerList_Peer) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[167]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use PeerList_Peer.ProtoReflect.Descriptor instead.\nfunc (*PeerList_Peer) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{75, 2}\n}\n\nfunc (x *PeerList_Peer) GetId() string {\n\tif x != nil {\n\t\treturn x.Id\n\t}\n\treturn \"\"\n}\n\nfunc (x *PeerList_Peer) GetRoutes() []*PeerList_Route {\n\tif x != nil {\n\t\treturn x.Routes\n\t}\n\treturn nil\n}\n\nfunc (x *PeerList_Peer) GetErrors() []string {\n\tif x != nil {\n\t\treturn x.Errors\n\t}\n\treturn nil\n}\n\nfunc (x *PeerList_Peer) GetFeatures() []PeerList_Feature {\n\tif x != nil {\n\t\treturn x.Features\n\t}\n\treturn nil\n}\n\nfunc (x *PeerList_Peer) GetMinLatency() int64 {\n\tif x != nil {\n\t\treturn x.MinLatency\n\t}\n\treturn 0\n}\n\nfunc (x *PeerList_Peer) GetIsActive() bool {\n\tif x != nil {\n\t\treturn x.IsActive\n\t}\n\treturn false\n}\n\nfunc (x *PeerList_Peer) GetDirection() Direction {\n\tif x != nil {\n\t\treturn x.Direction\n\t}\n\treturn Direction_UnknownDir\n}\n\ntype PeerList_Route struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// IsActive indicates whether the address is currently used or just known.\n\tIsActive bool `protobuf:\"varint,1,opt,name=is_active,json=isActive,proto3\" json:\"is_active,omitempty\"`\n\t// Address is the multiaddress via which we are connected with the peer.\n\tAddress string `protobuf:\"bytes,2,opt,name=address,proto3\" json:\"address,omitempty\"`\n\t// Direction is which way the connection was established.\n\tDirection Direction `protobuf:\"varint,3,opt,name=direction,proto3,enum=weshnet.protocol.v1.Direction\" json:\"direction,omitempty\"`\n\t// Latency is the last known round trip time to the peer in ms.\n\tLatency int64 `protobuf:\"varint,4,opt,name=latency,proto3\" json:\"latency,omitempty\"`\n\t// Streams returns list of streams established with the peer.\n\tStreams []*PeerList_Stream `protobuf:\"bytes,5,rep,name=streams,proto3\" json:\"streams,omitempty\"`\n}\n\nfunc (x *PeerList_Route) Reset() {\n\t*x = PeerList_Route{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[168]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *PeerList_Route) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*PeerList_Route) ProtoMessage() {}\n\nfunc (x *PeerList_Route) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[168]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use PeerList_Route.ProtoReflect.Descriptor instead.\nfunc (*PeerList_Route) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{75, 3}\n}\n\nfunc (x *PeerList_Route) GetIsActive() bool {\n\tif x != nil {\n\t\treturn x.IsActive\n\t}\n\treturn false\n}\n\nfunc (x *PeerList_Route) GetAddress() string {\n\tif x != nil {\n\t\treturn x.Address\n\t}\n\treturn \"\"\n}\n\nfunc (x *PeerList_Route) GetDirection() Direction {\n\tif x != nil {\n\t\treturn x.Direction\n\t}\n\treturn Direction_UnknownDir\n}\n\nfunc (x *PeerList_Route) GetLatency() int64 {\n\tif x != nil {\n\t\treturn x.Latency\n\t}\n\treturn 0\n}\n\nfunc (x *PeerList_Route) GetStreams() []*PeerList_Stream {\n\tif x != nil {\n\t\treturn x.Streams\n\t}\n\treturn nil\n}\n\ntype PeerList_Stream struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// id is an identifier used to write protocol headers in streams.\n\tId string `protobuf:\"bytes,1,opt,name=id,proto3\" json:\"id,omitempty\"`\n}\n\nfunc (x *PeerList_Stream) Reset() {\n\t*x = PeerList_Stream{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[169]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *PeerList_Stream) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*PeerList_Stream) ProtoMessage() {}\n\nfunc (x *PeerList_Stream) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[169]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use PeerList_Stream.ProtoReflect.Descriptor instead.\nfunc (*PeerList_Stream) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{75, 4}\n}\n\nfunc (x *PeerList_Stream) GetId() string {\n\tif x != nil {\n\t\treturn x.Id\n\t}\n\treturn \"\"\n}\n\ntype OutOfStoreReceive_Request struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tPayload []byte `protobuf:\"bytes,1,opt,name=payload,proto3\" json:\"payload,omitempty\"`\n}\n\nfunc (x *OutOfStoreReceive_Request) Reset() {\n\t*x = OutOfStoreReceive_Request{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[170]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *OutOfStoreReceive_Request) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*OutOfStoreReceive_Request) ProtoMessage() {}\n\nfunc (x *OutOfStoreReceive_Request) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[170]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use OutOfStoreReceive_Request.ProtoReflect.Descriptor instead.\nfunc (*OutOfStoreReceive_Request) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{79, 0}\n}\n\nfunc (x *OutOfStoreReceive_Request) GetPayload() []byte {\n\tif x != nil {\n\t\treturn x.Payload\n\t}\n\treturn nil\n}\n\ntype OutOfStoreReceive_Reply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tMessage         *OutOfStoreMessage `protobuf:\"bytes,1,opt,name=message,proto3\" json:\"message,omitempty\"`\n\tCleartext       []byte             `protobuf:\"bytes,2,opt,name=cleartext,proto3\" json:\"cleartext,omitempty\"`\n\tGroupPublicKey  []byte             `protobuf:\"bytes,3,opt,name=group_public_key,json=groupPublicKey,proto3\" json:\"group_public_key,omitempty\"`\n\tAlreadyReceived bool               `protobuf:\"varint,4,opt,name=already_received,json=alreadyReceived,proto3\" json:\"already_received,omitempty\"`\n}\n\nfunc (x *OutOfStoreReceive_Reply) Reset() {\n\t*x = OutOfStoreReceive_Reply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[171]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *OutOfStoreReceive_Reply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*OutOfStoreReceive_Reply) ProtoMessage() {}\n\nfunc (x *OutOfStoreReceive_Reply) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[171]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use OutOfStoreReceive_Reply.ProtoReflect.Descriptor instead.\nfunc (*OutOfStoreReceive_Reply) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{79, 1}\n}\n\nfunc (x *OutOfStoreReceive_Reply) GetMessage() *OutOfStoreMessage {\n\tif x != nil {\n\t\treturn x.Message\n\t}\n\treturn nil\n}\n\nfunc (x *OutOfStoreReceive_Reply) GetCleartext() []byte {\n\tif x != nil {\n\t\treturn x.Cleartext\n\t}\n\treturn nil\n}\n\nfunc (x *OutOfStoreReceive_Reply) GetGroupPublicKey() []byte {\n\tif x != nil {\n\t\treturn x.GroupPublicKey\n\t}\n\treturn nil\n}\n\nfunc (x *OutOfStoreReceive_Reply) GetAlreadyReceived() bool {\n\tif x != nil {\n\t\treturn x.AlreadyReceived\n\t}\n\treturn false\n}\n\ntype OutOfStoreSeal_Request struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tCid            []byte `protobuf:\"bytes,1,opt,name=cid,proto3\" json:\"cid,omitempty\"`\n\tGroupPublicKey []byte `protobuf:\"bytes,2,opt,name=group_public_key,json=groupPublicKey,proto3\" json:\"group_public_key,omitempty\"`\n}\n\nfunc (x *OutOfStoreSeal_Request) Reset() {\n\t*x = OutOfStoreSeal_Request{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[172]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *OutOfStoreSeal_Request) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*OutOfStoreSeal_Request) ProtoMessage() {}\n\nfunc (x *OutOfStoreSeal_Request) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[172]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use OutOfStoreSeal_Request.ProtoReflect.Descriptor instead.\nfunc (*OutOfStoreSeal_Request) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{80, 0}\n}\n\nfunc (x *OutOfStoreSeal_Request) GetCid() []byte {\n\tif x != nil {\n\t\treturn x.Cid\n\t}\n\treturn nil\n}\n\nfunc (x *OutOfStoreSeal_Request) GetGroupPublicKey() []byte {\n\tif x != nil {\n\t\treturn x.GroupPublicKey\n\t}\n\treturn nil\n}\n\ntype OutOfStoreSeal_Reply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tEncrypted []byte `protobuf:\"bytes,1,opt,name=encrypted,proto3\" json:\"encrypted,omitempty\"`\n}\n\nfunc (x *OutOfStoreSeal_Reply) Reset() {\n\t*x = OutOfStoreSeal_Reply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[173]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *OutOfStoreSeal_Reply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*OutOfStoreSeal_Reply) ProtoMessage() {}\n\nfunc (x *OutOfStoreSeal_Reply) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[173]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use OutOfStoreSeal_Reply.ProtoReflect.Descriptor instead.\nfunc (*OutOfStoreSeal_Reply) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{80, 1}\n}\n\nfunc (x *OutOfStoreSeal_Reply) GetEncrypted() []byte {\n\tif x != nil {\n\t\treturn x.Encrypted\n\t}\n\treturn nil\n}\n\ntype OrbitDBMessageHeads_Box struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tAddress  string `protobuf:\"bytes,1,opt,name=address,proto3\" json:\"address,omitempty\"`\n\tHeads    []byte `protobuf:\"bytes,2,opt,name=heads,proto3\" json:\"heads,omitempty\"`\n\tDevicePk []byte `protobuf:\"bytes,3,opt,name=device_pk,json=devicePk,proto3\" json:\"device_pk,omitempty\"`\n\tPeerId   []byte `protobuf:\"bytes,4,opt,name=peer_id,json=peerId,proto3\" json:\"peer_id,omitempty\"`\n}\n\nfunc (x *OrbitDBMessageHeads_Box) Reset() {\n\t*x = OrbitDBMessageHeads_Box{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[174]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *OrbitDBMessageHeads_Box) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*OrbitDBMessageHeads_Box) ProtoMessage() {}\n\nfunc (x *OrbitDBMessageHeads_Box) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[174]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use OrbitDBMessageHeads_Box.ProtoReflect.Descriptor instead.\nfunc (*OrbitDBMessageHeads_Box) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{83, 0}\n}\n\nfunc (x *OrbitDBMessageHeads_Box) GetAddress() string {\n\tif x != nil {\n\t\treturn x.Address\n\t}\n\treturn \"\"\n}\n\nfunc (x *OrbitDBMessageHeads_Box) GetHeads() []byte {\n\tif x != nil {\n\t\treturn x.Heads\n\t}\n\treturn nil\n}\n\nfunc (x *OrbitDBMessageHeads_Box) GetDevicePk() []byte {\n\tif x != nil {\n\t\treturn x.DevicePk\n\t}\n\treturn nil\n}\n\nfunc (x *OrbitDBMessageHeads_Box) GetPeerId() []byte {\n\tif x != nil {\n\t\treturn x.PeerId\n\t}\n\treturn nil\n}\n\ntype RefreshContactRequest_Peer struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// id is the libp2p.PeerID.\n\tId string `protobuf:\"bytes,1,opt,name=id,proto3\" json:\"id,omitempty\"`\n\t// list of peers multiaddrs.\n\tAddrs []string `protobuf:\"bytes,2,rep,name=addrs,proto3\" json:\"addrs,omitempty\"`\n}\n\nfunc (x *RefreshContactRequest_Peer) Reset() {\n\t*x = RefreshContactRequest_Peer{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[175]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *RefreshContactRequest_Peer) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*RefreshContactRequest_Peer) ProtoMessage() {}\n\nfunc (x *RefreshContactRequest_Peer) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[175]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use RefreshContactRequest_Peer.ProtoReflect.Descriptor instead.\nfunc (*RefreshContactRequest_Peer) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{84, 0}\n}\n\nfunc (x *RefreshContactRequest_Peer) GetId() string {\n\tif x != nil {\n\t\treturn x.Id\n\t}\n\treturn \"\"\n}\n\nfunc (x *RefreshContactRequest_Peer) GetAddrs() []string {\n\tif x != nil {\n\t\treturn x.Addrs\n\t}\n\treturn nil\n}\n\ntype RefreshContactRequest_Request struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tContactPk []byte `protobuf:\"bytes,1,opt,name=contact_pk,json=contactPk,proto3\" json:\"contact_pk,omitempty\"`\n\t// timeout in second\n\tTimeout int64 `protobuf:\"varint,2,opt,name=timeout,proto3\" json:\"timeout,omitempty\"`\n}\n\nfunc (x *RefreshContactRequest_Request) Reset() {\n\t*x = RefreshContactRequest_Request{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[176]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *RefreshContactRequest_Request) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*RefreshContactRequest_Request) ProtoMessage() {}\n\nfunc (x *RefreshContactRequest_Request) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[176]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use RefreshContactRequest_Request.ProtoReflect.Descriptor instead.\nfunc (*RefreshContactRequest_Request) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{84, 1}\n}\n\nfunc (x *RefreshContactRequest_Request) GetContactPk() []byte {\n\tif x != nil {\n\t\treturn x.ContactPk\n\t}\n\treturn nil\n}\n\nfunc (x *RefreshContactRequest_Request) GetTimeout() int64 {\n\tif x != nil {\n\t\treturn x.Timeout\n\t}\n\treturn 0\n}\n\ntype RefreshContactRequest_Reply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// peers found and successfully connected.\n\tPeersFound []*RefreshContactRequest_Peer `protobuf:\"bytes,1,rep,name=peers_found,json=peersFound,proto3\" json:\"peers_found,omitempty\"`\n}\n\nfunc (x *RefreshContactRequest_Reply) Reset() {\n\t*x = RefreshContactRequest_Reply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_protocoltypes_proto_msgTypes[177]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *RefreshContactRequest_Reply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*RefreshContactRequest_Reply) ProtoMessage() {}\n\nfunc (x *RefreshContactRequest_Reply) ProtoReflect() protoreflect.Message {\n\tmi := &file_protocoltypes_proto_msgTypes[177]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use RefreshContactRequest_Reply.ProtoReflect.Descriptor instead.\nfunc (*RefreshContactRequest_Reply) Descriptor() ([]byte, []int) {\n\treturn file_protocoltypes_proto_rawDescGZIP(), []int{84, 2}\n}\n\nfunc (x *RefreshContactRequest_Reply) GetPeersFound() []*RefreshContactRequest_Peer {\n\tif x != nil {\n\t\treturn x.PeersFound\n\t}\n\treturn nil\n}\n\nvar File_protocoltypes_proto protoreflect.FileDescriptor\n\nvar file_protocoltypes_proto_rawDesc = []byte{\n\t0x0a, 0x13, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e,\n\t0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x13, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70,\n\t0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x22, 0xcd, 0x01, 0x0a, 0x07, 0x41,\n\t0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x30, 0x0a, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18,\n\t0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e,\n\t0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75,\n\t0x70, 0x52, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x2e, 0x0a, 0x13, 0x61, 0x63, 0x63, 0x6f,\n\t0x75, 0x6e, 0x74, 0x5f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18,\n\t0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x11, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x50, 0x72,\n\t0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x2a, 0x0a, 0x11, 0x61, 0x6c, 0x69, 0x61,\n\t0x73, 0x5f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20,\n\t0x01, 0x28, 0x0c, 0x52, 0x0f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74,\n\t0x65, 0x4b, 0x65, 0x79, 0x12, 0x34, 0x0a, 0x16, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x72,\n\t0x65, 0x6e, 0x64, 0x65, 0x7a, 0x76, 0x6f, 0x75, 0x73, 0x5f, 0x73, 0x65, 0x65, 0x64, 0x18, 0x04,\n\t0x20, 0x01, 0x28, 0x0c, 0x52, 0x14, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x52, 0x65, 0x6e, 0x64,\n\t0x65, 0x7a, 0x76, 0x6f, 0x75, 0x73, 0x53, 0x65, 0x65, 0x64, 0x22, 0xf4, 0x01, 0x0a, 0x05, 0x47,\n\t0x72, 0x6f, 0x75, 0x70, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b,\n\t0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63,\n\t0x4b, 0x65, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, 0x20,\n\t0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73,\n\t0x65, 0x63, 0x72, 0x65, 0x74, 0x5f, 0x73, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52,\n\t0x09, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x53, 0x69, 0x67, 0x12, 0x3d, 0x0a, 0x0a, 0x67, 0x72,\n\t0x6f, 0x75, 0x70, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e,\n\t0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f,\n\t0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x54, 0x79, 0x70, 0x65, 0x52, 0x09,\n\t0x67, 0x72, 0x6f, 0x75, 0x70, 0x54, 0x79, 0x70, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x69, 0x67,\n\t0x6e, 0x5f, 0x70, 0x75, 0x62, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x73, 0x69, 0x67,\n\t0x6e, 0x50, 0x75, 0x62, 0x12, 0x19, 0x0a, 0x08, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x6b, 0x65, 0x79,\n\t0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6c, 0x69, 0x6e, 0x6b, 0x4b, 0x65, 0x79, 0x12,\n\t0x20, 0x0a, 0x0c, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x73, 0x69, 0x67, 0x18,\n\t0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x6c, 0x69, 0x6e, 0x6b, 0x4b, 0x65, 0x79, 0x53, 0x69,\n\t0x67, 0x22, 0xc7, 0x01, 0x0a, 0x10, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x48, 0x65, 0x61, 0x64, 0x73,\n\t0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63,\n\t0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c,\n\t0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x69, 0x67, 0x6e, 0x5f, 0x70, 0x75,\n\t0x62, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x73, 0x69, 0x67, 0x6e, 0x50, 0x75, 0x62,\n\t0x12, 0x2e, 0x0a, 0x13, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x68, 0x65, 0x61,\n\t0x64, 0x73, 0x5f, 0x63, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x11, 0x6d,\n\t0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x48, 0x65, 0x61, 0x64, 0x73, 0x43, 0x69, 0x64, 0x73,\n\t0x12, 0x2e, 0x0a, 0x13, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x5f, 0x68, 0x65, 0x61,\n\t0x64, 0x73, 0x5f, 0x63, 0x69, 0x64, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x11, 0x6d,\n\t0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x48, 0x65, 0x61, 0x64, 0x73, 0x43, 0x69, 0x64, 0x73,\n\t0x12, 0x19, 0x0a, 0x08, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x05, 0x20, 0x01,\n\t0x28, 0x0c, 0x52, 0x07, 0x6c, 0x69, 0x6e, 0x6b, 0x4b, 0x65, 0x79, 0x22, 0xce, 0x01, 0x0a, 0x0d,\n\t0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x3d, 0x0a,\n\t0x0a, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,\n\t0x0e, 0x32, 0x1e, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74,\n\t0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70,\n\t0x65, 0x52, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07,\n\t0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70,\n\t0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x69, 0x67, 0x18, 0x03, 0x20,\n\t0x01, 0x28, 0x0c, 0x52, 0x03, 0x73, 0x69, 0x67, 0x12, 0x52, 0x0a, 0x11, 0x70, 0x72, 0x6f, 0x74,\n\t0x6f, 0x63, 0x6f, 0x6c, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20,\n\t0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72,\n\t0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63,\n\t0x6f, 0x6c, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x10, 0x70, 0x72, 0x6f, 0x74,\n\t0x6f, 0x63, 0x6f, 0x6c, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0x41, 0x0a, 0x0d,\n\t0x47, 0x72, 0x6f, 0x75, 0x70, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x12, 0x14, 0x0a,\n\t0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x6e, 0x6f,\n\t0x6e, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01,\n\t0x28, 0x0c, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x22,\n\t0xe5, 0x01, 0x0a, 0x0e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65,\n\t0x72, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20,\n\t0x01, 0x28, 0x04, 0x52, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09,\n\t0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52,\n\t0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x69, 0x67,\n\t0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x73, 0x69, 0x67, 0x12, 0x4d, 0x0a, 0x08, 0x6d,\n\t0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e,\n\t0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c,\n\t0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65,\n\t0x72, 0x73, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79,\n\t0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x1a, 0x3b, 0x0a, 0x0d, 0x4d, 0x65,\n\t0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b,\n\t0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a,\n\t0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61,\n\t0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x18, 0x0a, 0x10, 0x50, 0x72, 0x6f, 0x74, 0x6f,\n\t0x63, 0x6f, 0x6c, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x4a, 0x04, 0x08, 0x01, 0x10,\n\t0x02, 0x22, 0x84, 0x01, 0x0a, 0x10, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d,\n\t0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x74,\n\t0x65, 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70, 0x6c, 0x61, 0x69, 0x6e,\n\t0x74, 0x65, 0x78, 0x74, 0x12, 0x52, 0x0a, 0x11, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c,\n\t0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,\n\t0x25, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63,\n\t0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x4d, 0x65,\n\t0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x10, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c,\n\t0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0x70, 0x0a, 0x0f, 0x4d, 0x65, 0x73, 0x73,\n\t0x61, 0x67, 0x65, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x6d,\n\t0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x01,\n\t0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x65, 0x61,\n\t0x64, 0x65, 0x72, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18,\n\t0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x14,\n\t0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x6e,\n\t0x6f, 0x6e, 0x63, 0x65, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x22, 0x5e, 0x0a, 0x0c, 0x45, 0x76,\n\t0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64,\n\t0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x61,\n\t0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x09,\n\t0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f,\n\t0x75, 0x70, 0x5f, 0x70, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x67, 0x72, 0x6f,\n\t0x75, 0x70, 0x50, 0x6b, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x22, 0x51, 0x0a, 0x18, 0x47, 0x72,\n\t0x6f, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x50, 0x61, 0x79, 0x6c, 0x6f,\n\t0x61, 0x64, 0x53, 0x65, 0x6e, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65,\n\t0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63,\n\t0x65, 0x50, 0x6b, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02,\n\t0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x4e, 0x0a,\n\t0x14, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x4b, 0x65, 0x79,\n\t0x41, 0x64, 0x64, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f,\n\t0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65,\n\t0x50, 0x6b, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x5f, 0x70, 0x6b, 0x18, 0x02,\n\t0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x50, 0x6b, 0x22, 0x71, 0x0a,\n\t0x16, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x44, 0x65, 0x76, 0x69,\n\t0x63, 0x65, 0x41, 0x64, 0x64, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x65, 0x6d, 0x62, 0x65,\n\t0x72, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x6d, 0x65, 0x6d, 0x62,\n\t0x65, 0x72, 0x50, 0x6b, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70,\n\t0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50,\n\t0x6b, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x73, 0x69, 0x67, 0x18,\n\t0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x53, 0x69, 0x67,\n\t0x22, 0x47, 0x0a, 0x0e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x4b,\n\t0x65, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x6b, 0x65, 0x79, 0x18,\n\t0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4b, 0x65, 0x79, 0x12,\n\t0x18, 0x0a, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04,\n\t0x52, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x22, 0x77, 0x0a, 0x18, 0x47, 0x72, 0x6f,\n\t0x75, 0x70, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x4b, 0x65, 0x79,\n\t0x41, 0x64, 0x64, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f,\n\t0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65,\n\t0x50, 0x6b, 0x12, 0x24, 0x0a, 0x0e, 0x64, 0x65, 0x73, 0x74, 0x5f, 0x6d, 0x65, 0x6d, 0x62, 0x65,\n\t0x72, 0x5f, 0x70, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x64, 0x65, 0x73, 0x74,\n\t0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x50, 0x6b, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c,\n\t0x6f, 0x61, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f,\n\t0x61, 0x64, 0x22, 0x89, 0x01, 0x0a, 0x22, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62,\n\t0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x65, 0x73, 0x6f,\n\t0x6c, 0x76, 0x65, 0x72, 0x41, 0x64, 0x64, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76,\n\t0x69, 0x63, 0x65, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65,\n\t0x76, 0x69, 0x63, 0x65, 0x50, 0x6b, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x5f,\n\t0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d,\n\t0x61, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x12, 0x1f, 0x0a,\n\t0x0b, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x03, 0x20, 0x01,\n\t0x28, 0x0c, 0x52, 0x0a, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x22, 0x6b,\n\t0x0a, 0x20, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f,\n\t0x75, 0x70, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74,\n\t0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6b, 0x18,\n\t0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6b, 0x12,\n\t0x2a, 0x0a, 0x11, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x65, 0x65, 0x5f, 0x6d, 0x65, 0x6d, 0x62, 0x65,\n\t0x72, 0x5f, 0x70, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x67, 0x72, 0x61, 0x6e,\n\t0x74, 0x65, 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x50, 0x6b, 0x22, 0x45, 0x0a, 0x26, 0x4d,\n\t0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49,\n\t0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x41, 0x6e, 0x6e, 0x6f,\n\t0x75, 0x6e, 0x63, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x5f,\n\t0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72,\n\t0x50, 0x6b, 0x22, 0x53, 0x0a, 0x20, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x64, 0x64, 0x41, 0x64,\n\t0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x52, 0x65, 0x6e, 0x64, 0x65, 0x7a, 0x76, 0x6f,\n\t0x75, 0x73, 0x53, 0x65, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65,\n\t0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63,\n\t0x65, 0x50, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x65, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28,\n\t0x0c, 0x52, 0x04, 0x73, 0x65, 0x65, 0x64, 0x22, 0x56, 0x0a, 0x23, 0x47, 0x72, 0x6f, 0x75, 0x70,\n\t0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c,\n\t0x52, 0x65, 0x6e, 0x64, 0x65, 0x7a, 0x76, 0x6f, 0x75, 0x73, 0x53, 0x65, 0x65, 0x64, 0x12, 0x1b,\n\t0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28,\n\t0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x73,\n\t0x65, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x73, 0x65, 0x65, 0x64, 0x22,\n\t0x63, 0x0a, 0x12, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4a,\n\t0x6f, 0x69, 0x6e, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f,\n\t0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65,\n\t0x50, 0x6b, 0x12, 0x30, 0x0a, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28,\n\t0x0b, 0x32, 0x1a, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74,\n\t0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x05, 0x67,\n\t0x72, 0x6f, 0x75, 0x70, 0x22, 0x4a, 0x0a, 0x10, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x47,\n\t0x72, 0x6f, 0x75, 0x70, 0x4c, 0x65, 0x66, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69,\n\t0x63, 0x65, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76,\n\t0x69, 0x63, 0x65, 0x50, 0x6b, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70,\n\t0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x6b,\n\t0x22, 0x3c, 0x0a, 0x1d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61,\n\t0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65,\n\t0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6b, 0x18, 0x01,\n\t0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6b, 0x22, 0x3b,\n\t0x0a, 0x1c, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74,\n\t0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1b,\n\t0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28,\n\t0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6b, 0x22, 0x78, 0x0a, 0x23, 0x41,\n\t0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71,\n\t0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73,\n\t0x65, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6b, 0x18,\n\t0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6b, 0x12,\n\t0x34, 0x0a, 0x16, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x7a,\n\t0x76, 0x6f, 0x75, 0x73, 0x5f, 0x73, 0x65, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52,\n\t0x14, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x52, 0x65, 0x6e, 0x64, 0x65, 0x7a, 0x76, 0x6f, 0x75,\n\t0x73, 0x53, 0x65, 0x65, 0x64, 0x22, 0xc3, 0x01, 0x0a, 0x25, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e,\n\t0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4f,\n\t0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x71, 0x75, 0x65, 0x75, 0x65, 0x64, 0x12,\n\t0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01,\n\t0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6b, 0x12, 0x19, 0x0a, 0x08,\n\t0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07,\n\t0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x6b, 0x12, 0x3f, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x61,\n\t0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e,\n\t0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53,\n\t0x68, 0x61, 0x72, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52,\n\t0x07, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x6f, 0x77, 0x6e, 0x5f,\n\t0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b,\n\t0x6f, 0x77, 0x6e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0x5f, 0x0a, 0x21, 0x41,\n\t0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71,\n\t0x75, 0x65, 0x73, 0x74, 0x4f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x6e, 0x74,\n\t0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20,\n\t0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6b, 0x12, 0x1d, 0x0a,\n\t0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x5f, 0x70, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28,\n\t0x0c, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x50, 0x6b, 0x22, 0xc6, 0x01, 0x0a,\n\t0x25, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52,\n\t0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x52, 0x65,\n\t0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65,\n\t0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63,\n\t0x65, 0x50, 0x6b, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x5f, 0x70,\n\t0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74,\n\t0x50, 0x6b, 0x12, 0x36, 0x0a, 0x17, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x5f, 0x72, 0x65,\n\t0x6e, 0x64, 0x65, 0x7a, 0x76, 0x6f, 0x75, 0x73, 0x5f, 0x73, 0x65, 0x65, 0x64, 0x18, 0x03, 0x20,\n\t0x01, 0x28, 0x0c, 0x52, 0x15, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x6e, 0x64,\n\t0x65, 0x7a, 0x76, 0x6f, 0x75, 0x73, 0x53, 0x65, 0x65, 0x64, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x6f,\n\t0x6e, 0x74, 0x61, 0x63, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04,\n\t0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x4d, 0x65, 0x74,\n\t0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0x64, 0x0a, 0x26, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74,\n\t0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x6e,\n\t0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x44, 0x69, 0x73, 0x63, 0x61, 0x72, 0x64, 0x65, 0x64, 0x12,\n\t0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01,\n\t0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6b, 0x12, 0x1d, 0x0a, 0x0a,\n\t0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x5f, 0x70, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c,\n\t0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x50, 0x6b, 0x22, 0x7e, 0x0a, 0x25, 0x41,\n\t0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71,\n\t0x75, 0x65, 0x73, 0x74, 0x49, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x41, 0x63, 0x63, 0x65,\n\t0x70, 0x74, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70,\n\t0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50,\n\t0x6b, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x5f, 0x70, 0x6b, 0x18,\n\t0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x50, 0x6b,\n\t0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, 0x6b, 0x18, 0x03, 0x20, 0x01,\n\t0x28, 0x0c, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x6b, 0x22, 0x53, 0x0a, 0x15, 0x41,\n\t0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x42, 0x6c, 0x6f,\n\t0x63, 0x6b, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70,\n\t0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50,\n\t0x6b, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x5f, 0x70, 0x6b, 0x18,\n\t0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x50, 0x6b,\n\t0x22, 0x55, 0x0a, 0x17, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61,\n\t0x63, 0x74, 0x55, 0x6e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64,\n\t0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08,\n\t0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6b, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74,\n\t0x61, 0x63, 0x74, 0x5f, 0x70, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6f,\n\t0x6e, 0x74, 0x61, 0x63, 0x74, 0x50, 0x6b, 0x22, 0x8d, 0x01, 0x0a, 0x10, 0x47, 0x72, 0x6f, 0x75,\n\t0x70, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x1b, 0x0a, 0x09,\n\t0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52,\n\t0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6b, 0x12, 0x2d, 0x0a, 0x12, 0x61, 0x75, 0x74,\n\t0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x75, 0x72, 0x6c, 0x18,\n\t0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63,\n\t0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x72, 0x6c, 0x12, 0x2d, 0x0a, 0x12, 0x72, 0x65, 0x70, 0x6c,\n\t0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x03,\n\t0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,\n\t0x6e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x22, 0x4c, 0x0a, 0x11, 0x53, 0x65, 0x72, 0x76, 0x69,\n\t0x63, 0x65, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x1a, 0x09, 0x0a, 0x07,\n\t0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79,\n\t0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x64, 0x61, 0x74,\n\t0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x65,\n\t0x64, 0x44, 0x61, 0x74, 0x61, 0x22, 0x93, 0x05, 0x0a, 0x17, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,\n\t0x65, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f,\n\t0x6e, 0x1a, 0x09, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0xa3, 0x04, 0x0a,\n\t0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e,\n\t0x74, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x61, 0x63, 0x63, 0x6f,\n\t0x75, 0x6e, 0x74, 0x50, 0x6b, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f,\n\t0x70, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65,\n\t0x50, 0x6b, 0x12, 0x28, 0x0a, 0x10, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x67, 0x72,\n\t0x6f, 0x75, 0x70, 0x5f, 0x70, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x61, 0x63,\n\t0x63, 0x6f, 0x75, 0x6e, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x6b, 0x12, 0x17, 0x0a, 0x07,\n\t0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70,\n\t0x65, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65,\n\t0x72, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e,\n\t0x65, 0x72, 0x73, 0x12, 0x5a, 0x0a, 0x0b, 0x62, 0x6c, 0x65, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c,\n\t0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x39, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e,\n\t0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53,\n\t0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75,\n\t0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x74,\n\t0x61, 0x74, 0x65, 0x52, 0x0a, 0x62, 0x6c, 0x65, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12,\n\t0x63, 0x0a, 0x10, 0x77, 0x69, 0x66, 0x69, 0x5f, 0x70, 0x32, 0x70, 0x5f, 0x65, 0x6e, 0x61, 0x62,\n\t0x6c, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x39, 0x2e, 0x77, 0x65, 0x73, 0x68,\n\t0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e,\n\t0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,\n\t0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x53,\n\t0x74, 0x61, 0x74, 0x65, 0x52, 0x0e, 0x77, 0x69, 0x66, 0x69, 0x50, 0x32, 0x70, 0x45, 0x6e, 0x61,\n\t0x62, 0x6c, 0x65, 0x64, 0x12, 0x5c, 0x0a, 0x0c, 0x6d, 0x64, 0x6e, 0x73, 0x5f, 0x65, 0x6e, 0x61,\n\t0x62, 0x6c, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x39, 0x2e, 0x77, 0x65, 0x73,\n\t0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31,\n\t0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69,\n\t0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67,\n\t0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0b, 0x6d, 0x64, 0x6e, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c,\n\t0x65, 0x64, 0x12, 0x5e, 0x0a, 0x0d, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x5f, 0x65, 0x6e, 0x61, 0x62,\n\t0x6c, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x39, 0x2e, 0x77, 0x65, 0x73, 0x68,\n\t0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e,\n\t0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,\n\t0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x53,\n\t0x74, 0x61, 0x74, 0x65, 0x52, 0x0c, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x45, 0x6e, 0x61, 0x62, 0x6c,\n\t0x65, 0x64, 0x22, 0x47, 0x0a, 0x0c, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61,\n\t0x74, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12,\n\t0x0b, 0x0a, 0x07, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08,\n\t0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x10, 0x02, 0x12, 0x0f, 0x0a, 0x0b, 0x55, 0x6e,\n\t0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x10, 0x03, 0x22, 0x7d, 0x0a, 0x17, 0x43,\n\t0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x66,\n\t0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x1a, 0x09, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,\n\t0x74, 0x1a, 0x57, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x34, 0x0a, 0x16, 0x70, 0x75,\n\t0x62, 0x6c, 0x69, 0x63, 0x5f, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x7a, 0x76, 0x6f, 0x75, 0x73, 0x5f,\n\t0x73, 0x65, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x14, 0x70, 0x75, 0x62, 0x6c,\n\t0x69, 0x63, 0x52, 0x65, 0x6e, 0x64, 0x65, 0x7a, 0x76, 0x6f, 0x75, 0x73, 0x53, 0x65, 0x65, 0x64,\n\t0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28,\n\t0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x2b, 0x0a, 0x15, 0x43, 0x6f,\n\t0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x44, 0x69, 0x73, 0x61,\n\t0x62, 0x6c, 0x65, 0x1a, 0x09, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x07,\n\t0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x60, 0x0a, 0x14, 0x43, 0x6f, 0x6e, 0x74, 0x61,\n\t0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x1a,\n\t0x09, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3d, 0x0a, 0x05, 0x52, 0x65,\n\t0x70, 0x6c, 0x79, 0x12, 0x34, 0x0a, 0x16, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x72, 0x65,\n\t0x6e, 0x64, 0x65, 0x7a, 0x76, 0x6f, 0x75, 0x73, 0x5f, 0x73, 0x65, 0x65, 0x64, 0x18, 0x01, 0x20,\n\t0x01, 0x28, 0x0c, 0x52, 0x14, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x52, 0x65, 0x6e, 0x64, 0x65,\n\t0x7a, 0x76, 0x6f, 0x75, 0x73, 0x53, 0x65, 0x65, 0x64, 0x22, 0x68, 0x0a, 0x1c, 0x43, 0x6f, 0x6e,\n\t0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x65, 0x74,\n\t0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x1a, 0x09, 0x0a, 0x07, 0x52, 0x65, 0x71,\n\t0x75, 0x65, 0x73, 0x74, 0x1a, 0x3d, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x34, 0x0a,\n\t0x16, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x7a, 0x76, 0x6f,\n\t0x75, 0x73, 0x5f, 0x73, 0x65, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x14, 0x70,\n\t0x75, 0x62, 0x6c, 0x69, 0x63, 0x52, 0x65, 0x6e, 0x64, 0x65, 0x7a, 0x76, 0x6f, 0x75, 0x73, 0x53,\n\t0x65, 0x65, 0x64, 0x22, 0x8c, 0x01, 0x0a, 0x12, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52,\n\t0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x65, 0x6e, 0x64, 0x1a, 0x6d, 0x0a, 0x07, 0x52, 0x65,\n\t0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74,\n\t0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74,\n\t0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x68, 0x61,\n\t0x72, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x07, 0x63,\n\t0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x6f, 0x77, 0x6e, 0x5f, 0x6d, 0x65,\n\t0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x6f, 0x77,\n\t0x6e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x1a, 0x07, 0x0a, 0x05, 0x52, 0x65, 0x70,\n\t0x6c, 0x79, 0x22, 0x49, 0x0a, 0x14, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71,\n\t0x75, 0x65, 0x73, 0x74, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x1a, 0x28, 0x0a, 0x07, 0x52, 0x65,\n\t0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74,\n\t0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61,\n\t0x63, 0x74, 0x50, 0x6b, 0x1a, 0x07, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x4a, 0x0a,\n\t0x15, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x44,\n\t0x69, 0x73, 0x63, 0x61, 0x72, 0x64, 0x1a, 0x28, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,\n\t0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x5f, 0x70, 0x6b, 0x18,\n\t0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x50, 0x6b,\n\t0x1a, 0x07, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x4b, 0x0a, 0x0c, 0x53, 0x68, 0x61,\n\t0x72, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x1a, 0x09, 0x0a, 0x07, 0x52, 0x65, 0x71,\n\t0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x27, 0x0a,\n\t0x0f, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74,\n\t0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x43,\n\t0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x22, 0x8d, 0x01, 0x0a, 0x0d, 0x44, 0x65, 0x63, 0x6f, 0x64,\n\t0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x1a, 0x32, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75,\n\t0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x5f, 0x63,\n\t0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x65, 0x6e,\n\t0x63, 0x6f, 0x64, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x1a, 0x48, 0x0a, 0x05,\n\t0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x3f, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74,\n\t0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74,\n\t0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x68, 0x61,\n\t0x72, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x07, 0x63,\n\t0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x22, 0x41, 0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63,\n\t0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x1a, 0x28, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,\n\t0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x5f, 0x70, 0x6b, 0x18,\n\t0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x50, 0x6b,\n\t0x1a, 0x07, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x43, 0x0a, 0x0e, 0x43, 0x6f, 0x6e,\n\t0x74, 0x61, 0x63, 0x74, 0x55, 0x6e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x1a, 0x28, 0x0a, 0x07, 0x52,\n\t0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63,\n\t0x74, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74,\n\t0x61, 0x63, 0x74, 0x50, 0x6b, 0x1a, 0x07, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x44,\n\t0x0a, 0x13, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x4b, 0x65,\n\t0x79, 0x53, 0x65, 0x6e, 0x64, 0x1a, 0x24, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,\n\t0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01,\n\t0x28, 0x0c, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x6b, 0x1a, 0x07, 0x0a, 0x05, 0x52,\n\t0x65, 0x70, 0x6c, 0x79, 0x22, 0x47, 0x0a, 0x16, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d,\n\t0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x1a, 0x09,\n\t0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x0a, 0x05, 0x52, 0x65, 0x70,\n\t0x6c, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, 0x6b, 0x18, 0x01,\n\t0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x6b, 0x22, 0x5c, 0x0a,\n\t0x14, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75,\n\t0x70, 0x4a, 0x6f, 0x69, 0x6e, 0x1a, 0x3b, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,\n\t0x12, 0x30, 0x0a, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,\n\t0x1a, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63,\n\t0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x05, 0x67, 0x72, 0x6f,\n\t0x75, 0x70, 0x1a, 0x07, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x46, 0x0a, 0x15, 0x4d,\n\t0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4c,\n\t0x65, 0x61, 0x76, 0x65, 0x1a, 0x24, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,\n\t0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28,\n\t0x0c, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x6b, 0x1a, 0x07, 0x0a, 0x05, 0x52, 0x65,\n\t0x70, 0x6c, 0x79, 0x22, 0x56, 0x0a, 0x25, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62,\n\t0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x65, 0x73, 0x6f,\n\t0x6c, 0x76, 0x65, 0x72, 0x44, 0x69, 0x73, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x1a, 0x24, 0x0a, 0x07,\n\t0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70,\n\t0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70,\n\t0x50, 0x6b, 0x1a, 0x07, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x6c, 0x0a, 0x1e, 0x4d,\n\t0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41,\n\t0x64, 0x6d, 0x69, 0x6e, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x1a, 0x41, 0x0a,\n\t0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75,\n\t0x70, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75,\n\t0x70, 0x50, 0x6b, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x70, 0x6b,\n\t0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x50, 0x6b,\n\t0x1a, 0x07, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x83, 0x01, 0x0a, 0x20, 0x4d, 0x75,\n\t0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x6e,\n\t0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x1a, 0x24,\n\t0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f,\n\t0x75, 0x70, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x67, 0x72, 0x6f,\n\t0x75, 0x70, 0x50, 0x6b, 0x1a, 0x39, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x30, 0x0a,\n\t0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x77,\n\t0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e,\n\t0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x22,\n\t0x72, 0x0a, 0x0f, 0x41, 0x70, 0x70, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x53, 0x65,\n\t0x6e, 0x64, 0x1a, 0x44, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a,\n\t0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52,\n\t0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x6b, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c,\n\t0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f,\n\t0x61, 0x64, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x1a, 0x19, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c,\n\t0x79, 0x12, 0x10, 0x0a, 0x03, 0x63, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03,\n\t0x63, 0x69, 0x64, 0x22, 0x71, 0x0a, 0x0e, 0x41, 0x70, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67,\n\t0x65, 0x53, 0x65, 0x6e, 0x64, 0x1a, 0x44, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,\n\t0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01,\n\t0x28, 0x0c, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x6b, 0x12, 0x18, 0x0a, 0x07, 0x70,\n\t0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61,\n\t0x79, 0x6c, 0x6f, 0x61, 0x64, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x1a, 0x19, 0x0a, 0x05, 0x52,\n\t0x65, 0x70, 0x6c, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x63, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,\n\t0x0c, 0x52, 0x03, 0x63, 0x69, 0x64, 0x22, 0xb2, 0x01, 0x0a, 0x12, 0x47, 0x72, 0x6f, 0x75, 0x70,\n\t0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x46, 0x0a,\n\t0x0d, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01,\n\t0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70,\n\t0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74,\n\t0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52, 0x0c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x43, 0x6f,\n\t0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x3e, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,\n\t0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65,\n\t0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72,\n\t0x6f, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74,\n\t0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x03,\n\t0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0xb4, 0x01, 0x0a, 0x11,\n\t0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x45, 0x76, 0x65, 0x6e,\n\t0x74, 0x12, 0x46, 0x0a, 0x0d, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65,\n\t0x78, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e,\n\t0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x45,\n\t0x76, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52, 0x0c, 0x65, 0x76, 0x65,\n\t0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x3d, 0x0a, 0x07, 0x68, 0x65, 0x61,\n\t0x64, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x77, 0x65, 0x73,\n\t0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31,\n\t0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x52,\n\t0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73,\n\t0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61,\n\t0x67, 0x65, 0x22, 0xcf, 0x01, 0x0a, 0x11, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x61,\n\t0x64, 0x61, 0x74, 0x61, 0x4c, 0x69, 0x73, 0x74, 0x1a, 0xb9, 0x01, 0x0a, 0x07, 0x52, 0x65, 0x71,\n\t0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, 0x6b,\n\t0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x6b, 0x12,\n\t0x19, 0x0a, 0x08, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28,\n\t0x0c, 0x52, 0x07, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x69,\n\t0x6e, 0x63, 0x65, 0x5f, 0x6e, 0x6f, 0x77, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x73,\n\t0x69, 0x6e, 0x63, 0x65, 0x4e, 0x6f, 0x77, 0x12, 0x19, 0x0a, 0x08, 0x75, 0x6e, 0x74, 0x69, 0x6c,\n\t0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x75, 0x6e, 0x74, 0x69, 0x6c,\n\t0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x75, 0x6e, 0x74, 0x69, 0x6c, 0x5f, 0x6e, 0x6f, 0x77, 0x18,\n\t0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x75, 0x6e, 0x74, 0x69, 0x6c, 0x4e, 0x6f, 0x77, 0x12,\n\t0x23, 0x0a, 0x0d, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72,\n\t0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x4f,\n\t0x72, 0x64, 0x65, 0x72, 0x22, 0xce, 0x01, 0x0a, 0x10, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65,\n\t0x73, 0x73, 0x61, 0x67, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x1a, 0xb9, 0x01, 0x0a, 0x07, 0x52, 0x65,\n\t0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70,\n\t0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x6b,\n\t0x12, 0x19, 0x0a, 0x08, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01,\n\t0x28, 0x0c, 0x52, 0x07, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x73,\n\t0x69, 0x6e, 0x63, 0x65, 0x5f, 0x6e, 0x6f, 0x77, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08,\n\t0x73, 0x69, 0x6e, 0x63, 0x65, 0x4e, 0x6f, 0x77, 0x12, 0x19, 0x0a, 0x08, 0x75, 0x6e, 0x74, 0x69,\n\t0x6c, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x75, 0x6e, 0x74, 0x69,\n\t0x6c, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x75, 0x6e, 0x74, 0x69, 0x6c, 0x5f, 0x6e, 0x6f, 0x77,\n\t0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x75, 0x6e, 0x74, 0x69, 0x6c, 0x4e, 0x6f, 0x77,\n\t0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x5f, 0x6f, 0x72, 0x64, 0x65,\n\t0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65,\n\t0x4f, 0x72, 0x64, 0x65, 0x72, 0x22, 0xc5, 0x01, 0x0a, 0x09, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49,\n\t0x6e, 0x66, 0x6f, 0x1a, 0x43, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19,\n\t0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c,\n\t0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x6b, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x6e,\n\t0x74, 0x61, 0x63, 0x74, 0x5f, 0x70, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63,\n\t0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x50, 0x6b, 0x1a, 0x73, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c,\n\t0x79, 0x12, 0x30, 0x0a, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,\n\t0x32, 0x1a, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,\n\t0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x05, 0x67, 0x72,\n\t0x6f, 0x75, 0x70, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x70, 0x6b,\n\t0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x50, 0x6b,\n\t0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6b, 0x18, 0x03, 0x20,\n\t0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6b, 0x22, 0x5d, 0x0a,\n\t0x0d, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x1a, 0x43,\n\t0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f,\n\t0x75, 0x70, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x67, 0x72, 0x6f,\n\t0x75, 0x70, 0x50, 0x6b, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x6f, 0x6e,\n\t0x6c, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x4f,\n\t0x6e, 0x6c, 0x79, 0x1a, 0x07, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x40, 0x0a, 0x0f,\n\t0x44, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x1a,\n\t0x24, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72,\n\t0x6f, 0x75, 0x70, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x67, 0x72,\n\t0x6f, 0x75, 0x70, 0x50, 0x6b, 0x1a, 0x07, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0xd1,\n\t0x04, 0x0a, 0x11, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x53, 0x74,\n\t0x61, 0x74, 0x75, 0x73, 0x1a, 0x24, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,\n\t0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28,\n\t0x0c, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x6b, 0x1a, 0xea, 0x02, 0x0a, 0x05, 0x52,\n\t0x65, 0x70, 0x6c, 0x79, 0x12, 0x3f, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01,\n\t0x28, 0x0e, 0x32, 0x2b, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f,\n\t0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x44, 0x65,\n\t0x76, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52,\n\t0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x02,\n\t0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x1a, 0xaf, 0x01, 0x0a, 0x0d,\n\t0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x17, 0x0a,\n\t0x07, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,\n\t0x70, 0x65, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65,\n\t0x5f, 0x70, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63,\n\t0x65, 0x50, 0x6b, 0x12, 0x50, 0x0a, 0x0a, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74,\n\t0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x30, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65,\n\t0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72,\n\t0x6f, 0x75, 0x70, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e,\n\t0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x0a, 0x74, 0x72, 0x61, 0x6e, 0x73,\n\t0x70, 0x6f, 0x72, 0x74, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x61, 0x64, 0x64, 0x72, 0x73, 0x18,\n\t0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x61, 0x64, 0x64, 0x72, 0x73, 0x1a, 0x2b, 0x0a,\n\t0x10, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6e,\n\t0x67, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01,\n\t0x28, 0x09, 0x52, 0x06, 0x70, 0x65, 0x65, 0x72, 0x49, 0x64, 0x1a, 0x2b, 0x0a, 0x10, 0x50, 0x65,\n\t0x65, 0x72, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x17,\n\t0x0a, 0x07, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,\n\t0x06, 0x70, 0x65, 0x65, 0x72, 0x49, 0x64, 0x22, 0x62, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12,\n\t0x0f, 0x0a, 0x0b, 0x54, 0x79, 0x70, 0x65, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00,\n\t0x12, 0x18, 0x0a, 0x14, 0x54, 0x79, 0x70, 0x65, 0x50, 0x65, 0x65, 0x72, 0x44, 0x69, 0x73, 0x63,\n\t0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x10, 0x01, 0x12, 0x15, 0x0a, 0x11, 0x54, 0x79,\n\t0x70, 0x65, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x10,\n\t0x02, 0x12, 0x18, 0x0a, 0x14, 0x54, 0x79, 0x70, 0x65, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x63,\n\t0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6e, 0x67, 0x10, 0x03, 0x22, 0x45, 0x0a, 0x09, 0x54,\n\t0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x0e, 0x0a, 0x0a, 0x54, 0x70, 0x74, 0x55,\n\t0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x54, 0x70, 0x74, 0x4c,\n\t0x41, 0x4e, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x54, 0x70, 0x74, 0x57, 0x41, 0x4e, 0x10, 0x02,\n\t0x12, 0x10, 0x0a, 0x0c, 0x54, 0x70, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x69, 0x6d, 0x69, 0x74, 0x79,\n\t0x10, 0x03, 0x22, 0x9f, 0x01, 0x0a, 0x0f, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x69, 0x73, 0x74,\n\t0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x1a, 0x09, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,\n\t0x74, 0x1a, 0x80, 0x01, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x67,\n\t0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x67,\n\t0x72, 0x6f, 0x75, 0x70, 0x50, 0x6b, 0x12, 0x3d, 0x0a, 0x0a, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f,\n\t0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x77, 0x65, 0x73,\n\t0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31,\n\t0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x54, 0x79, 0x70, 0x65, 0x52, 0x09, 0x67, 0x72, 0x6f, 0x75,\n\t0x70, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74,\n\t0x5f, 0x70, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61,\n\t0x63, 0x74, 0x50, 0x6b, 0x22, 0xcc, 0x02, 0x0a, 0x16, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e,\n\t0x73, 0x70, 0x65, 0x63, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x1a,\n\t0x6e, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72,\n\t0x6f, 0x75, 0x70, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x67, 0x72,\n\t0x6f, 0x75, 0x70, 0x50, 0x6b, 0x12, 0x48, 0x0a, 0x08, 0x6c, 0x6f, 0x67, 0x5f, 0x74, 0x79, 0x70,\n\t0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2d, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65,\n\t0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65,\n\t0x62, 0x75, 0x67, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4c,\n\t0x6f, 0x67, 0x54, 0x79, 0x70, 0x65, 0x52, 0x07, 0x6c, 0x6f, 0x67, 0x54, 0x79, 0x70, 0x65, 0x1a,\n\t0xc1, 0x01, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x63, 0x69, 0x64,\n\t0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x63, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x70,\n\t0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c,\n\t0x52, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x43, 0x69, 0x64, 0x73, 0x12, 0x4e, 0x0a, 0x13,\n\t0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x74,\n\t0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x77, 0x65, 0x73, 0x68,\n\t0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e,\n\t0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x11, 0x6d, 0x65, 0x74, 0x61, 0x64,\n\t0x61, 0x74, 0x61, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09,\n\t0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52,\n\t0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6b, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79,\n\t0x6c, 0x6f, 0x61, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c,\n\t0x6f, 0x61, 0x64, 0x22, 0x56, 0x0a, 0x0a, 0x44, 0x65, 0x62, 0x75, 0x67, 0x47, 0x72, 0x6f, 0x75,\n\t0x70, 0x1a, 0x24, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08,\n\t0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07,\n\t0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x6b, 0x1a, 0x22, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79,\n\t0x12, 0x19, 0x0a, 0x08, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03,\n\t0x28, 0x09, 0x52, 0x07, 0x70, 0x65, 0x65, 0x72, 0x49, 0x64, 0x73, 0x22, 0x74, 0x0a, 0x10, 0x53,\n\t0x68, 0x61, 0x72, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x12,\n\t0x0e, 0x0a, 0x02, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x70, 0x6b, 0x12,\n\t0x34, 0x0a, 0x16, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x7a,\n\t0x76, 0x6f, 0x75, 0x73, 0x5f, 0x73, 0x65, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52,\n\t0x14, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x52, 0x65, 0x6e, 0x64, 0x65, 0x7a, 0x76, 0x6f, 0x75,\n\t0x73, 0x53, 0x65, 0x65, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,\n\t0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,\n\t0x61, 0x22, 0x6c, 0x0a, 0x1c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x54, 0x6f, 0x6b, 0x65,\n\t0x6e, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,\n\t0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70,\n\t0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,\n\t0x54, 0x79, 0x70, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f,\n\t0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f,\n\t0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x22,\n\t0xd5, 0x01, 0x0a, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e,\n\t0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,\n\t0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x2d, 0x0a, 0x12, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e,\n\t0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01,\n\t0x28, 0x09, 0x52, 0x11, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69,\n\t0x6f, 0x6e, 0x55, 0x72, 0x6c, 0x12, 0x60, 0x0a, 0x12, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74,\n\t0x65, 0x64, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28,\n\t0x0b, 0x32, 0x31, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74,\n\t0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x54,\n\t0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x53, 0x65, 0x72,\n\t0x76, 0x69, 0x63, 0x65, 0x52, 0x11, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x53,\n\t0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72,\n\t0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x65, 0x78, 0x70,\n\t0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xc0, 0x01, 0x0a, 0x25, 0x43, 0x72, 0x65, 0x64,\n\t0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69,\n\t0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x69, 0x74, 0x46, 0x6c, 0x6f,\n\t0x77, 0x1a, 0x5d, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b,\n\t0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28,\n\t0x09, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x72, 0x6c, 0x12, 0x1d, 0x0a,\n\t0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28,\n\t0x0c, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x12, 0x0a, 0x04,\n\t0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6c, 0x69, 0x6e, 0x6b,\n\t0x1a, 0x38, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c,\n\t0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x73,\n\t0x65, 0x63, 0x75, 0x72, 0x65, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52,\n\t0x09, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x55, 0x72, 0x6c, 0x22, 0x82, 0x01, 0x0a, 0x29, 0x43,\n\t0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63,\n\t0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6f, 0x6d, 0x70,\n\t0x6c, 0x65, 0x74, 0x65, 0x46, 0x6c, 0x6f, 0x77, 0x1a, 0x2c, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75,\n\t0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5f,\n\t0x75, 0x72, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x61, 0x6c, 0x6c, 0x62,\n\t0x61, 0x63, 0x6b, 0x55, 0x72, 0x69, 0x1a, 0x27, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12,\n\t0x1e, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20,\n\t0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x22,\n\t0x83, 0x02, 0x0a, 0x17, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x43, 0x72, 0x65, 0x64,\n\t0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x1a, 0x84, 0x01, 0x0a, 0x07,\n\t0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x11, 0x66, 0x69, 0x6c, 0x74, 0x65,\n\t0x72, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01,\n\t0x28, 0x09, 0x52, 0x10, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69,\n\t0x66, 0x69, 0x65, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x69,\n\t0x73, 0x73, 0x75, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x66, 0x69, 0x6c,\n\t0x74, 0x65, 0x72, 0x49, 0x73, 0x73, 0x75, 0x65, 0x72, 0x12, 0x27, 0x0a, 0x0f, 0x65, 0x78, 0x63,\n\t0x6c, 0x75, 0x64, 0x65, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01,\n\t0x28, 0x08, 0x52, 0x0e, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x45, 0x78, 0x70, 0x69, 0x72,\n\t0x65, 0x64, 0x1a, 0x61, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x58, 0x0a, 0x0a, 0x63,\n\t0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,\n\t0x38, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63,\n\t0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x56, 0x65, 0x72,\n\t0x69, 0x66, 0x69, 0x65, 0x64, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52,\n\t0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x64, 0x65,\n\t0x6e, 0x74, 0x69, 0x61, 0x6c, 0x22, 0xc5, 0x01, 0x0a, 0x1f, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63,\n\t0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x67, 0x69,\n\t0x73, 0x74, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x1a, 0x98, 0x01, 0x0a, 0x07, 0x52, 0x65,\n\t0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70,\n\t0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x6b,\n\t0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,\n\t0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x2d, 0x0a, 0x12, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e,\n\t0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01,\n\t0x28, 0x09, 0x52, 0x11, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69,\n\t0x6f, 0x6e, 0x55, 0x72, 0x6c, 0x12, 0x2d, 0x0a, 0x12, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61,\n\t0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28,\n\t0x09, 0x52, 0x11, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65,\n\t0x72, 0x76, 0x65, 0x72, 0x1a, 0x07, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x78, 0x0a,\n\t0x20, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76,\n\t0x69, 0x63, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75,\n\t0x70, 0x1a, 0x3b, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x30, 0x0a, 0x05,\n\t0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x77, 0x65,\n\t0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76,\n\t0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x1a, 0x17,\n\t0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x6f, 0x6b, 0x18, 0x01, 0x20,\n\t0x01, 0x28, 0x08, 0x52, 0x02, 0x6f, 0x6b, 0x22, 0xc2, 0x09, 0x0a, 0x0a, 0x53, 0x79, 0x73, 0x74,\n\t0x65, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x1a, 0x09, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,\n\t0x74, 0x1a, 0xda, 0x01, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x41, 0x0a, 0x07, 0x70,\n\t0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x77,\n\t0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e,\n\t0x76, 0x31, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x50, 0x72,\n\t0x6f, 0x63, 0x65, 0x73, 0x73, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x12, 0x35,\n\t0x0a, 0x03, 0x70, 0x32, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x77, 0x65,\n\t0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76,\n\t0x31, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x50, 0x32, 0x50,\n\t0x52, 0x03, 0x70, 0x32, 0x70, 0x12, 0x41, 0x0a, 0x07, 0x6f, 0x72, 0x62, 0x69, 0x74, 0x64, 0x62,\n\t0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74,\n\t0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x79, 0x73,\n\t0x74, 0x65, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x4f, 0x72, 0x62, 0x69, 0x74, 0x44, 0x42, 0x52,\n\t0x07, 0x6f, 0x72, 0x62, 0x69, 0x74, 0x64, 0x62, 0x12, 0x14, 0x0a, 0x05, 0x77, 0x61, 0x72, 0x6e,\n\t0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x77, 0x61, 0x72, 0x6e, 0x73, 0x1a, 0xee,\n\t0x01, 0x0a, 0x07, 0x4f, 0x72, 0x62, 0x69, 0x74, 0x44, 0x42, 0x12, 0x64, 0x0a, 0x10, 0x61, 0x63,\n\t0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01,\n\t0x20, 0x01, 0x28, 0x0b, 0x32, 0x39, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70,\n\t0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65,\n\t0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x4f, 0x72, 0x62, 0x69, 0x74, 0x44, 0x42, 0x2e, 0x52, 0x65,\n\t0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52,\n\t0x0f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,\n\t0x1a, 0x7d, 0x0a, 0x11, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53,\n\t0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73,\n\t0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73,\n\t0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x18, 0x02, 0x20, 0x01,\n\t0x28, 0x03, 0x52, 0x07, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x12, 0x1a, 0x0a, 0x08, 0x62,\n\t0x75, 0x66, 0x66, 0x65, 0x72, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x62,\n\t0x75, 0x66, 0x66, 0x65, 0x72, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x71, 0x75, 0x65, 0x75, 0x65,\n\t0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x71, 0x75, 0x65, 0x75, 0x65, 0x64, 0x1a,\n\t0x2e, 0x0a, 0x03, 0x50, 0x32, 0x50, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63,\n\t0x74, 0x65, 0x64, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52,\n\t0x0e, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x73, 0x1a,\n\t0xaa, 0x05, 0x0a, 0x07, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x76,\n\t0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65,\n\t0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x07, 0x76, 0x63, 0x73, 0x5f, 0x72, 0x65, 0x66,\n\t0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x76, 0x63, 0x73, 0x52, 0x65, 0x66, 0x12, 0x1b,\n\t0x0a, 0x09, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28,\n\t0x03, 0x52, 0x08, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x4d, 0x73, 0x12, 0x27, 0x0a, 0x10, 0x75,\n\t0x73, 0x65, 0x72, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x73, 0x18,\n\t0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x75, 0x73, 0x65, 0x72, 0x43, 0x70, 0x75, 0x54, 0x69,\n\t0x6d, 0x65, 0x4d, 0x73, 0x12, 0x2b, 0x0a, 0x12, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x63,\n\t0x70, 0x75, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03,\n\t0x52, 0x0f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x43, 0x70, 0x75, 0x54, 0x69, 0x6d, 0x65, 0x4d,\n\t0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18,\n\t0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x41, 0x74,\n\t0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x63, 0x75, 0x72, 0x18, 0x0d,\n\t0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x72, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x43, 0x75, 0x72, 0x12,\n\t0x23, 0x0a, 0x0d, 0x6e, 0x75, 0x6d, 0x5f, 0x67, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65,\n\t0x18, 0x0e, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x6e, 0x75, 0x6d, 0x47, 0x6f, 0x72, 0x6f, 0x75,\n\t0x74, 0x69, 0x6e, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6e, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x0f,\n\t0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6e, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x2d, 0x0a, 0x13,\n\t0x74, 0x6f, 0x6f, 0x5f, 0x6d, 0x61, 0x6e, 0x79, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x66, 0x69,\n\t0x6c, 0x65, 0x73, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x74, 0x6f, 0x6f, 0x4d, 0x61,\n\t0x6e, 0x79, 0x4f, 0x70, 0x65, 0x6e, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x6e,\n\t0x75, 0x6d, 0x5f, 0x63, 0x70, 0x75, 0x18, 0x11, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6e, 0x75,\n\t0x6d, 0x43, 0x70, 0x75, 0x12, 0x1d, 0x0a, 0x0a, 0x67, 0x6f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69,\n\t0x6f, 0x6e, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x67, 0x6f, 0x56, 0x65, 0x72, 0x73,\n\t0x69, 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6e, 0x67,\n\t0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x18, 0x13, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6f,\n\t0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1b,\n\t0x0a, 0x09, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28,\n\t0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x61,\n\t0x72, 0x63, 0x68, 0x18, 0x15, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x72, 0x63, 0x68, 0x12,\n\t0x1d, 0x0a, 0x0a, 0x72, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x6d, 0x61, 0x78, 0x18, 0x16, 0x20,\n\t0x01, 0x28, 0x04, 0x52, 0x09, 0x72, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x4d, 0x61, 0x78, 0x12, 0x10,\n\t0x0a, 0x03, 0x70, 0x69, 0x64, 0x18, 0x17, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x70, 0x69, 0x64,\n\t0x12, 0x12, 0x0a, 0x04, 0x70, 0x70, 0x69, 0x64, 0x18, 0x18, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04,\n\t0x70, 0x70, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79,\n\t0x18, 0x19, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79,\n\t0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x75,\n\t0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x69,\n\t0x72, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x77, 0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67,\n\t0x44, 0x69, 0x72, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x75, 0x73,\n\t0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x79,\n\t0x73, 0x74, 0x65, 0x6d, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0xeb, 0x05, 0x0a,\n\t0x08, 0x50, 0x65, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x1a, 0x09, 0x0a, 0x07, 0x52, 0x65, 0x71,\n\t0x75, 0x65, 0x73, 0x74, 0x1a, 0x41, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x38, 0x0a,\n\t0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x77,\n\t0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e,\n\t0x76, 0x31, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x2e, 0x50, 0x65, 0x65, 0x72,\n\t0x52, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x1a, 0xaa, 0x02, 0x0a, 0x04, 0x50, 0x65, 0x65, 0x72,\n\t0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64,\n\t0x12, 0x3b, 0x0a, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b,\n\t0x32, 0x23, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,\n\t0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x2e,\n\t0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x16, 0x0a,\n\t0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x65,\n\t0x72, 0x72, 0x6f, 0x72, 0x73, 0x12, 0x41, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65,\n\t0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x25, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65,\n\t0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x65,\n\t0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x08,\n\t0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x69, 0x6e, 0x5f,\n\t0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x6d,\n\t0x69, 0x6e, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x73, 0x5f,\n\t0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x69, 0x73,\n\t0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x12, 0x3c, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74,\n\t0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x77, 0x65, 0x73, 0x68,\n\t0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e,\n\t0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63,\n\t0x74, 0x69, 0x6f, 0x6e, 0x1a, 0xd6, 0x01, 0x0a, 0x05, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x1b,\n\t0x0a, 0x09, 0x69, 0x73, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,\n\t0x08, 0x52, 0x08, 0x69, 0x73, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61,\n\t0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64,\n\t0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x3c, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69,\n\t0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e,\n\t0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x44,\n\t0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74,\n\t0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x04,\n\t0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x3e, 0x0a,\n\t0x07, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24,\n\t0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f,\n\t0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x2e, 0x53, 0x74,\n\t0x72, 0x65, 0x61, 0x6d, 0x52, 0x07, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x73, 0x1a, 0x18, 0x0a,\n\t0x06, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20,\n\t0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x71, 0x0a, 0x07, 0x46, 0x65, 0x61, 0x74, 0x75,\n\t0x72, 0x65, 0x12, 0x12, 0x0a, 0x0e, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x46, 0x65, 0x61,\n\t0x74, 0x75, 0x72, 0x65, 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x57, 0x65, 0x73, 0x68, 0x46, 0x65,\n\t0x61, 0x74, 0x75, 0x72, 0x65, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x42, 0x4c, 0x45, 0x46, 0x65,\n\t0x61, 0x74, 0x75, 0x72, 0x65, 0x10, 0x02, 0x12, 0x10, 0x0a, 0x0c, 0x4c, 0x6f, 0x63, 0x61, 0x6c,\n\t0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x10, 0x03, 0x12, 0x0e, 0x0a, 0x0a, 0x54, 0x6f, 0x72,\n\t0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x10, 0x04, 0x12, 0x0f, 0x0a, 0x0b, 0x51, 0x75, 0x69,\n\t0x63, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x10, 0x05, 0x22, 0x9c, 0x01, 0x0a, 0x08, 0x50,\n\t0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65,\n\t0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x14, 0x0a,\n\t0x05, 0x64, 0x6f, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x64, 0x6f,\n\t0x69, 0x6e, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x18,\n\t0x03, 0x20, 0x01, 0x28, 0x02, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x12,\n\t0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01,\n\t0x28, 0x04, 0x52, 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, 0x14, 0x0a,\n\t0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x74, 0x6f,\n\t0x74, 0x61, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x06, 0x20, 0x01,\n\t0x28, 0x04, 0x52, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x22, 0xc7, 0x01, 0x0a, 0x11, 0x4f, 0x75,\n\t0x74, 0x4f, 0x66, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12,\n\t0x10, 0x0a, 0x03, 0x63, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x63, 0x69,\n\t0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6b, 0x18, 0x02,\n\t0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6b, 0x12, 0x18,\n\t0x0a, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x06, 0x52,\n\t0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x69, 0x67, 0x18,\n\t0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x73, 0x69, 0x67, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6c,\n\t0x61, 0x67, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x07, 0x52, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73,\n\t0x12, 0x2b, 0x0a, 0x11, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x70, 0x61,\n\t0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x65, 0x6e, 0x63,\n\t0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x14, 0x0a,\n\t0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x6e, 0x6f,\n\t0x6e, 0x63, 0x65, 0x22, 0x6c, 0x0a, 0x19, 0x4f, 0x75, 0x74, 0x4f, 0x66, 0x53, 0x74, 0x6f, 0x72,\n\t0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65,\n\t0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52,\n\t0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x62, 0x6f, 0x78, 0x18, 0x02, 0x20,\n\t0x01, 0x28, 0x0c, 0x52, 0x03, 0x62, 0x6f, 0x78, 0x12, 0x27, 0x0a, 0x0f, 0x67, 0x72, 0x6f, 0x75,\n\t0x70, 0x5f, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28,\n\t0x0c, 0x52, 0x0e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63,\n\t0x65, 0x22, 0xf7, 0x01, 0x0a, 0x11, 0x4f, 0x75, 0x74, 0x4f, 0x66, 0x53, 0x74, 0x6f, 0x72, 0x65,\n\t0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x1a, 0x23, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65,\n\t0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, 0x20,\n\t0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x1a, 0xbc, 0x01, 0x0a,\n\t0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x40, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67,\n\t0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65,\n\t0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75,\n\t0x74, 0x4f, 0x66, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52,\n\t0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6c, 0x65, 0x61,\n\t0x72, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6c, 0x65,\n\t0x61, 0x72, 0x74, 0x65, 0x78, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f,\n\t0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c,\n\t0x52, 0x0e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79,\n\t0x12, 0x29, 0x0a, 0x10, 0x61, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x5f, 0x72, 0x65, 0x63, 0x65,\n\t0x69, 0x76, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x61, 0x6c, 0x72, 0x65,\n\t0x61, 0x64, 0x79, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x22, 0x7e, 0x0a, 0x0e, 0x4f,\n\t0x75, 0x74, 0x4f, 0x66, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x53, 0x65, 0x61, 0x6c, 0x1a, 0x45, 0x0a,\n\t0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x63, 0x69, 0x64, 0x18,\n\t0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x63, 0x69, 0x64, 0x12, 0x28, 0x0a, 0x10, 0x67, 0x72,\n\t0x6f, 0x75, 0x70, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02,\n\t0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x75, 0x62, 0x6c, 0x69,\n\t0x63, 0x4b, 0x65, 0x79, 0x1a, 0x25, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1c, 0x0a,\n\t0x09, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c,\n\t0x52, 0x09, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x22, 0xbe, 0x02, 0x0a, 0x23,\n\t0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x43,\n\t0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65,\n\t0x72, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6b,\n\t0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6b,\n\t0x12, 0x3b, 0x0a, 0x1a, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74,\n\t0x69, 0x74, 0x79, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02,\n\t0x20, 0x01, 0x28, 0x0c, 0x52, 0x17, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x49, 0x64, 0x65, 0x6e,\n\t0x74, 0x69, 0x74, 0x79, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x2f, 0x0a,\n\t0x13, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e,\n\t0x74, 0x69, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x76, 0x65, 0x72, 0x69,\n\t0x66, 0x69, 0x65, 0x64, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x2b,\n\t0x0a, 0x11, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64,\n\t0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x72, 0x65, 0x67, 0x69, 0x73,\n\t0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x65,\n\t0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x05,\n\t0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,\n\t0x44, 0x61, 0x74, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69,\n\t0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69,\n\t0x66, 0x69, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x73, 0x73, 0x75, 0x65, 0x72, 0x18, 0x07,\n\t0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x69, 0x73, 0x73, 0x75, 0x65, 0x72, 0x22, 0x3d, 0x0a, 0x11,\n\t0x46, 0x69, 0x72, 0x73, 0x74, 0x4c, 0x61, 0x73, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72,\n\t0x73, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x69, 0x72, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04,\n\t0x52, 0x05, 0x66, 0x69, 0x72, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x61, 0x73, 0x74, 0x18,\n\t0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x6c, 0x61, 0x73, 0x74, 0x22, 0xc4, 0x01, 0x0a, 0x13,\n\t0x4f, 0x72, 0x62, 0x69, 0x74, 0x44, 0x42, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x65,\n\t0x61, 0x64, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x61, 0x6c, 0x65, 0x64, 0x5f, 0x62, 0x6f,\n\t0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x65, 0x61, 0x6c, 0x65, 0x64, 0x42,\n\t0x6f, 0x78, 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x61, 0x77, 0x5f, 0x72, 0x6f, 0x74, 0x61, 0x74, 0x69,\n\t0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x72, 0x61, 0x77, 0x52, 0x6f, 0x74,\n\t0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x6b, 0x0a, 0x03, 0x42, 0x6f, 0x78, 0x12, 0x18, 0x0a, 0x07,\n\t0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61,\n\t0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x68, 0x65, 0x61, 0x64, 0x73, 0x18,\n\t0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x68, 0x65, 0x61, 0x64, 0x73, 0x12, 0x1b, 0x0a, 0x09,\n\t0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52,\n\t0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6b, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x65, 0x65,\n\t0x72, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70, 0x65, 0x65, 0x72,\n\t0x49, 0x64, 0x22, 0xe4, 0x01, 0x0a, 0x15, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x43, 0x6f,\n\t0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x0a, 0x04,\n\t0x50, 0x65, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,\n\t0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x64, 0x64, 0x72, 0x73, 0x18, 0x02, 0x20,\n\t0x03, 0x28, 0x09, 0x52, 0x05, 0x61, 0x64, 0x64, 0x72, 0x73, 0x1a, 0x42, 0x0a, 0x07, 0x52, 0x65,\n\t0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74,\n\t0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61,\n\t0x63, 0x74, 0x50, 0x6b, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18,\n\t0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x1a, 0x59,\n\t0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x50, 0x0a, 0x0b, 0x70, 0x65, 0x65, 0x72, 0x73,\n\t0x5f, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x77,\n\t0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e,\n\t0x76, 0x31, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63,\n\t0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x52, 0x0a, 0x70,\n\t0x65, 0x65, 0x72, 0x73, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x2a, 0x69, 0x0a, 0x09, 0x47, 0x72, 0x6f,\n\t0x75, 0x70, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x12, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x54,\n\t0x79, 0x70, 0x65, 0x55, 0x6e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x10, 0x00, 0x12, 0x14,\n\t0x0a, 0x10, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x54, 0x79, 0x70, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75,\n\t0x6e, 0x74, 0x10, 0x01, 0x12, 0x14, 0x0a, 0x10, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x54, 0x79, 0x70,\n\t0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x10, 0x02, 0x12, 0x18, 0x0a, 0x14, 0x47, 0x72,\n\t0x6f, 0x75, 0x70, 0x54, 0x79, 0x70, 0x65, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62,\n\t0x65, 0x72, 0x10, 0x03, 0x2a, 0xba, 0x07, 0x0a, 0x09, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79,\n\t0x70, 0x65, 0x12, 0x16, 0x0a, 0x12, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x55,\n\t0x6e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x10, 0x00, 0x12, 0x23, 0x0a, 0x1f, 0x45, 0x76,\n\t0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62,\n\t0x65, 0x72, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x64, 0x64, 0x65, 0x64, 0x10, 0x01, 0x12,\n\t0x25, 0x0a, 0x21, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x47, 0x72, 0x6f, 0x75,\n\t0x70, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x4b, 0x65, 0x79, 0x41,\n\t0x64, 0x64, 0x65, 0x64, 0x10, 0x02, 0x12, 0x1f, 0x0a, 0x1b, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54,\n\t0x79, 0x70, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4a,\n\t0x6f, 0x69, 0x6e, 0x65, 0x64, 0x10, 0x65, 0x12, 0x1d, 0x0a, 0x19, 0x45, 0x76, 0x65, 0x6e, 0x74,\n\t0x54, 0x79, 0x70, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70,\n\t0x4c, 0x65, 0x66, 0x74, 0x10, 0x66, 0x12, 0x2a, 0x0a, 0x26, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54,\n\t0x79, 0x70, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63,\n\t0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64,\n\t0x10, 0x67, 0x12, 0x29, 0x0a, 0x25, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x41,\n\t0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71,\n\t0x75, 0x65, 0x73, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x10, 0x68, 0x12, 0x30, 0x0a,\n\t0x2c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e,\n\t0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52,\n\t0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x65, 0x74, 0x10, 0x69, 0x12,\n\t0x32, 0x0a, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x41, 0x63, 0x63, 0x6f,\n\t0x75, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,\n\t0x74, 0x4f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x71, 0x75, 0x65, 0x75, 0x65,\n\t0x64, 0x10, 0x6a, 0x12, 0x2e, 0x0a, 0x2a, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65,\n\t0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65,\n\t0x71, 0x75, 0x65, 0x73, 0x74, 0x4f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x6e,\n\t0x74, 0x10, 0x6b, 0x12, 0x32, 0x0a, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65,\n\t0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65,\n\t0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x63,\n\t0x65, 0x69, 0x76, 0x65, 0x64, 0x10, 0x6c, 0x12, 0x33, 0x0a, 0x2f, 0x45, 0x76, 0x65, 0x6e, 0x74,\n\t0x54, 0x79, 0x70, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61,\n\t0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e,\n\t0x67, 0x44, 0x69, 0x73, 0x63, 0x61, 0x72, 0x64, 0x65, 0x64, 0x10, 0x6d, 0x12, 0x32, 0x0a, 0x2e,\n\t0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74,\n\t0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x6e,\n\t0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x10, 0x6e,\n\t0x12, 0x22, 0x0a, 0x1e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x41, 0x63, 0x63,\n\t0x6f, 0x75, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b,\n\t0x65, 0x64, 0x10, 0x6f, 0x12, 0x24, 0x0a, 0x20, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70,\n\t0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x55,\n\t0x6e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x10, 0x70, 0x12, 0x22, 0x0a, 0x1d, 0x45, 0x76,\n\t0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x41, 0x6c,\n\t0x69, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x41, 0x64, 0x64, 0x65, 0x64, 0x10, 0xc9, 0x01, 0x12, 0x30,\n\t0x0a, 0x2b, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x4d, 0x75, 0x6c, 0x74, 0x69,\n\t0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x6c, 0x69, 0x61, 0x73,\n\t0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x41, 0x64, 0x64, 0x65, 0x64, 0x10, 0xad, 0x02,\n\t0x12, 0x34, 0x0a, 0x2f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x4d, 0x75, 0x6c,\n\t0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x6e, 0x69,\n\t0x74, 0x69, 0x61, 0x6c, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e,\n\t0x63, 0x65, 0x64, 0x10, 0xae, 0x02, 0x12, 0x2e, 0x0a, 0x29, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54,\n\t0x79, 0x70, 0x65, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72,\n\t0x6f, 0x75, 0x70, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e,\n\t0x74, 0x65, 0x64, 0x10, 0xaf, 0x02, 0x12, 0x1e, 0x0a, 0x19, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54,\n\t0x79, 0x70, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74,\n\t0x69, 0x6e, 0x67, 0x10, 0x93, 0x03, 0x12, 0x31, 0x0a, 0x2c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54,\n\t0x79, 0x70, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69,\n\t0x65, 0x64, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x65, 0x67, 0x69,\n\t0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x10, 0xf4, 0x03, 0x12, 0x26, 0x0a, 0x21, 0x45, 0x76, 0x65,\n\t0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x61, 0x64,\n\t0x61, 0x74, 0x61, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x65, 0x6e, 0x74, 0x10, 0xe9,\n\t0x07, 0x2a, 0x8c, 0x01, 0x0a, 0x18, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x73, 0x70, 0x65,\n\t0x63, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4c, 0x6f, 0x67, 0x54, 0x79, 0x70, 0x65, 0x12, 0x25,\n\t0x0a, 0x21, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x47, 0x72,\n\t0x6f, 0x75, 0x70, 0x4c, 0x6f, 0x67, 0x54, 0x79, 0x70, 0x65, 0x55, 0x6e, 0x64, 0x65, 0x66, 0x69,\n\t0x6e, 0x65, 0x64, 0x10, 0x00, 0x12, 0x23, 0x0a, 0x1f, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e,\n\t0x73, 0x70, 0x65, 0x63, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4c, 0x6f, 0x67, 0x54, 0x79, 0x70,\n\t0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x10, 0x01, 0x12, 0x24, 0x0a, 0x20, 0x44, 0x65,\n\t0x62, 0x75, 0x67, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4c,\n\t0x6f, 0x67, 0x54, 0x79, 0x70, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x10, 0x02,\n\t0x2a, 0xc2, 0x01, 0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x53, 0x74, 0x61, 0x74,\n\t0x65, 0x12, 0x19, 0x0a, 0x15, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x53, 0x74, 0x61, 0x74,\n\t0x65, 0x55, 0x6e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15,\n\t0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x6f, 0x52, 0x65,\n\t0x71, 0x75, 0x65, 0x73, 0x74, 0x10, 0x01, 0x12, 0x18, 0x0a, 0x14, 0x43, 0x6f, 0x6e, 0x74, 0x61,\n\t0x63, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x10,\n\t0x02, 0x12, 0x15, 0x0a, 0x11, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x53, 0x74, 0x61, 0x74,\n\t0x65, 0x41, 0x64, 0x64, 0x65, 0x64, 0x10, 0x03, 0x12, 0x17, 0x0a, 0x13, 0x43, 0x6f, 0x6e, 0x74,\n\t0x61, 0x63, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x10,\n\t0x04, 0x12, 0x19, 0x0a, 0x15, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x53, 0x74, 0x61, 0x74,\n\t0x65, 0x44, 0x69, 0x73, 0x63, 0x61, 0x72, 0x64, 0x65, 0x64, 0x10, 0x05, 0x12, 0x17, 0x0a, 0x13,\n\t0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x42, 0x6c, 0x6f, 0x63,\n\t0x6b, 0x65, 0x64, 0x10, 0x06, 0x2a, 0x47, 0x0a, 0x09, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69,\n\t0x6f, 0x6e, 0x12, 0x0e, 0x0a, 0x0a, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x44, 0x69, 0x72,\n\t0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x44, 0x69, 0x72,\n\t0x10, 0x01, 0x12, 0x0f, 0x0a, 0x0b, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x44, 0x69,\n\t0x72, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x42, 0x69, 0x44, 0x69, 0x72, 0x10, 0x03, 0x32, 0xdd,\n\t0x26, 0x0a, 0x0f, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x53, 0x65, 0x72, 0x76, 0x69,\n\t0x63, 0x65, 0x12, 0x73, 0x0a, 0x11, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x45, 0x78, 0x70,\n\t0x6f, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x12, 0x2e, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65,\n\t0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65,\n\t0x72, 0x76, 0x69, 0x63, 0x65, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x2e,\n\t0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65,\n\t0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65,\n\t0x72, 0x76, 0x69, 0x63, 0x65, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x2e,\n\t0x52, 0x65, 0x70, 0x6c, 0x79, 0x30, 0x01, 0x12, 0x83, 0x01, 0x0a, 0x17, 0x53, 0x65, 0x72, 0x76,\n\t0x69, 0x63, 0x65, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74,\n\t0x69, 0x6f, 0x6e, 0x12, 0x34, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72,\n\t0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,\n\t0x65, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f,\n\t0x6e, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x77, 0x65, 0x73, 0x68,\n\t0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e,\n\t0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,\n\t0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x83, 0x01,\n\t0x0a, 0x17, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,\n\t0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x34, 0x2e, 0x77, 0x65, 0x73, 0x68,\n\t0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e,\n\t0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65,\n\t0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,\n\t0x32, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63,\n\t0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71,\n\t0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x2e, 0x52, 0x65,\n\t0x70, 0x6c, 0x79, 0x12, 0x7d, 0x0a, 0x15, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65,\n\t0x71, 0x75, 0x65, 0x73, 0x74, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x32, 0x2e, 0x77,\n\t0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e,\n\t0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,\n\t0x74, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,\n\t0x1a, 0x30, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,\n\t0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65,\n\t0x71, 0x75, 0x65, 0x73, 0x74, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x52, 0x65, 0x70,\n\t0x6c, 0x79, 0x12, 0x7a, 0x0a, 0x14, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71,\n\t0x75, 0x65, 0x73, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x31, 0x2e, 0x77, 0x65, 0x73,\n\t0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31,\n\t0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x45,\n\t0x6e, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e,\n\t0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c,\n\t0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65,\n\t0x73, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x92,\n\t0x01, 0x0a, 0x1c, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,\n\t0x74, 0x52, 0x65, 0x73, 0x65, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12,\n\t0x39, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63,\n\t0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71,\n\t0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x65, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e,\n\t0x63, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x77, 0x65, 0x73,\n\t0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31,\n\t0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52,\n\t0x65, 0x73, 0x65, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x2e, 0x52, 0x65,\n\t0x70, 0x6c, 0x79, 0x12, 0x74, 0x0a, 0x12, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65,\n\t0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x65, 0x6e, 0x64, 0x12, 0x2f, 0x2e, 0x77, 0x65, 0x73, 0x68,\n\t0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e,\n\t0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x65,\n\t0x6e, 0x64, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x77, 0x65, 0x73,\n\t0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31,\n\t0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53,\n\t0x65, 0x6e, 0x64, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x7a, 0x0a, 0x14, 0x43, 0x6f, 0x6e,\n\t0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x41, 0x63, 0x63, 0x65, 0x70,\n\t0x74, 0x12, 0x31, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74,\n\t0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52,\n\t0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2e, 0x52, 0x65, 0x71,\n\t0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70,\n\t0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61,\n\t0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2e,\n\t0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x7d, 0x0a, 0x15, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74,\n\t0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x44, 0x69, 0x73, 0x63, 0x61, 0x72, 0x64, 0x12, 0x32,\n\t0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f,\n\t0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75,\n\t0x65, 0x73, 0x74, 0x44, 0x69, 0x73, 0x63, 0x61, 0x72, 0x64, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65,\n\t0x73, 0x74, 0x1a, 0x30, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f,\n\t0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74,\n\t0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x44, 0x69, 0x73, 0x63, 0x61, 0x72, 0x64, 0x2e, 0x52,\n\t0x65, 0x70, 0x6c, 0x79, 0x12, 0x62, 0x0a, 0x0c, 0x53, 0x68, 0x61, 0x72, 0x65, 0x43, 0x6f, 0x6e,\n\t0x74, 0x61, 0x63, 0x74, 0x12, 0x29, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70,\n\t0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x65,\n\t0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,\n\t0x27, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63,\n\t0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61,\n\t0x63, 0x74, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x65, 0x0a, 0x0d, 0x44, 0x65, 0x63, 0x6f,\n\t0x64, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x12, 0x2a, 0x2e, 0x77, 0x65, 0x73, 0x68,\n\t0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e,\n\t0x44, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x2e, 0x52, 0x65,\n\t0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e,\n\t0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x63, 0x6f,\n\t0x64, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12,\n\t0x62, 0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12,\n\t0x29, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63,\n\t0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x42, 0x6c, 0x6f,\n\t0x63, 0x6b, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x77, 0x65, 0x73,\n\t0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31,\n\t0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x52, 0x65,\n\t0x70, 0x6c, 0x79, 0x12, 0x68, 0x0a, 0x0e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x55, 0x6e,\n\t0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x2b, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e,\n\t0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74,\n\t0x61, 0x63, 0x74, 0x55, 0x6e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65,\n\t0x73, 0x74, 0x1a, 0x29, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f,\n\t0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74,\n\t0x55, 0x6e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x77, 0x0a,\n\t0x13, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x4b, 0x65, 0x79,\n\t0x53, 0x65, 0x6e, 0x64, 0x12, 0x30, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70,\n\t0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61,\n\t0x63, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x53, 0x65, 0x6e, 0x64, 0x2e, 0x52,\n\t0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74,\n\t0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e,\n\t0x74, 0x61, 0x63, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x53, 0x65, 0x6e, 0x64,\n\t0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x80, 0x01, 0x0a, 0x16, 0x4d, 0x75, 0x6c, 0x74, 0x69,\n\t0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x43, 0x72, 0x65, 0x61, 0x74,\n\t0x65, 0x12, 0x33, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74,\n\t0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d,\n\t0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x2e, 0x52,\n\t0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74,\n\t0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x75, 0x6c,\n\t0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x43, 0x72, 0x65,\n\t0x61, 0x74, 0x65, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x7a, 0x0a, 0x14, 0x4d, 0x75, 0x6c,\n\t0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4a, 0x6f, 0x69,\n\t0x6e, 0x12, 0x31, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74,\n\t0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d,\n\t0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4a, 0x6f, 0x69, 0x6e, 0x2e, 0x52, 0x65, 0x71,\n\t0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70,\n\t0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69,\n\t0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4a, 0x6f, 0x69, 0x6e, 0x2e,\n\t0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x7d, 0x0a, 0x15, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65,\n\t0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x12, 0x32,\n\t0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f,\n\t0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72,\n\t0x47, 0x72, 0x6f, 0x75, 0x70, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65,\n\t0x73, 0x74, 0x1a, 0x30, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f,\n\t0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65,\n\t0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x2e, 0x52,\n\t0x65, 0x70, 0x6c, 0x79, 0x12, 0xad, 0x01, 0x0a, 0x25, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65,\n\t0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x65,\n\t0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x44, 0x69, 0x73, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x12, 0x42,\n\t0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f,\n\t0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72,\n\t0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76,\n\t0x65, 0x72, 0x44, 0x69, 0x73, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65,\n\t0x73, 0x74, 0x1a, 0x40, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f,\n\t0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65,\n\t0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x65,\n\t0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x44, 0x69, 0x73, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x2e, 0x52,\n\t0x65, 0x70, 0x6c, 0x79, 0x12, 0x98, 0x01, 0x0a, 0x1e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65,\n\t0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x52, 0x6f,\n\t0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x12, 0x3b, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65,\n\t0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x75,\n\t0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x64,\n\t0x6d, 0x69, 0x6e, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x71,\n\t0x75, 0x65, 0x73, 0x74, 0x1a, 0x39, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70,\n\t0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69,\n\t0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x64, 0x6d, 0x69, 0x6e,\n\t0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12,\n\t0x9e, 0x01, 0x0a, 0x20, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47,\n\t0x72, 0x6f, 0x75, 0x70, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x72,\n\t0x65, 0x61, 0x74, 0x65, 0x12, 0x3d, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70,\n\t0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69,\n\t0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x6e, 0x76, 0x69, 0x74,\n\t0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75,\n\t0x65, 0x73, 0x74, 0x1a, 0x3b, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72,\n\t0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d,\n\t0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x61,\n\t0x74, 0x69, 0x6f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79,\n\t0x12, 0x6b, 0x0a, 0x0f, 0x41, 0x70, 0x70, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x53,\n\t0x65, 0x6e, 0x64, 0x12, 0x2c, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72,\n\t0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x70, 0x70, 0x4d, 0x65, 0x74,\n\t0x61, 0x64, 0x61, 0x74, 0x61, 0x53, 0x65, 0x6e, 0x64, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,\n\t0x74, 0x1a, 0x2a, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74,\n\t0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x70, 0x70, 0x4d, 0x65, 0x74, 0x61, 0x64,\n\t0x61, 0x74, 0x61, 0x53, 0x65, 0x6e, 0x64, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x68, 0x0a,\n\t0x0e, 0x41, 0x70, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x53, 0x65, 0x6e, 0x64, 0x12,\n\t0x2b, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63,\n\t0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x70, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,\n\t0x53, 0x65, 0x6e, 0x64, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x77,\n\t0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e,\n\t0x76, 0x31, 0x2e, 0x41, 0x70, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x53, 0x65, 0x6e,\n\t0x64, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x6e, 0x0a, 0x11, 0x47, 0x72, 0x6f, 0x75, 0x70,\n\t0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x2e, 0x2e, 0x77,\n\t0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e,\n\t0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,\n\t0x4c, 0x69, 0x73, 0x74, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x77,\n\t0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e,\n\t0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,\n\t0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x6b, 0x0a, 0x10, 0x47, 0x72, 0x6f, 0x75, 0x70,\n\t0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x2d, 0x2e, 0x77, 0x65,\n\t0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76,\n\t0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4c, 0x69,\n\t0x73, 0x74, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x77, 0x65, 0x73,\n\t0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31,\n\t0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x45, 0x76, 0x65,\n\t0x6e, 0x74, 0x30, 0x01, 0x12, 0x59, 0x0a, 0x09, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x6e, 0x66,\n\t0x6f, 0x12, 0x26, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74,\n\t0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x6e, 0x66,\n\t0x6f, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x77, 0x65, 0x73, 0x68,\n\t0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e,\n\t0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12,\n\t0x65, 0x0a, 0x0d, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70,\n\t0x12, 0x2a, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,\n\t0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x47,\n\t0x72, 0x6f, 0x75, 0x70, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x77,\n\t0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e,\n\t0x76, 0x31, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70,\n\t0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x6b, 0x0a, 0x0f, 0x44, 0x65, 0x61, 0x63, 0x74, 0x69,\n\t0x76, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x2c, 0x2e, 0x77, 0x65, 0x73, 0x68,\n\t0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e,\n\t0x44, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x2e,\n\t0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65,\n\t0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65,\n\t0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x52, 0x65,\n\t0x70, 0x6c, 0x79, 0x12, 0x73, 0x0a, 0x11, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x44, 0x65, 0x76, 0x69,\n\t0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2e, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e,\n\t0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47,\n\t0x72, 0x6f, 0x75, 0x70, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73,\n\t0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e,\n\t0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47,\n\t0x72, 0x6f, 0x75, 0x70, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73,\n\t0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x30, 0x01, 0x12, 0x6d, 0x0a, 0x0f, 0x44, 0x65, 0x62, 0x75,\n\t0x67, 0x4c, 0x69, 0x73, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x2c, 0x2e, 0x77, 0x65,\n\t0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76,\n\t0x31, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x69, 0x73, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70,\n\t0x73, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x77, 0x65, 0x73, 0x68,\n\t0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e,\n\t0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x69, 0x73, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e,\n\t0x52, 0x65, 0x70, 0x6c, 0x79, 0x30, 0x01, 0x12, 0x82, 0x01, 0x0a, 0x16, 0x44, 0x65, 0x62, 0x75,\n\t0x67, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x74, 0x6f,\n\t0x72, 0x65, 0x12, 0x33, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f,\n\t0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e,\n\t0x73, 0x70, 0x65, 0x63, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x2e,\n\t0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65,\n\t0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65,\n\t0x62, 0x75, 0x67, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53,\n\t0x74, 0x6f, 0x72, 0x65, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x30, 0x01, 0x12, 0x5c, 0x0a, 0x0a,\n\t0x44, 0x65, 0x62, 0x75, 0x67, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x27, 0x2e, 0x77, 0x65, 0x73,\n\t0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31,\n\t0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x52, 0x65, 0x71, 0x75,\n\t0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72,\n\t0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x47,\n\t0x72, 0x6f, 0x75, 0x70, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x5c, 0x0a, 0x0a, 0x53, 0x79,\n\t0x73, 0x74, 0x65, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x27, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e,\n\t0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53,\n\t0x79, 0x73, 0x74, 0x65, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,\n\t0x74, 0x1a, 0x25, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74,\n\t0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x49, 0x6e,\n\t0x66, 0x6f, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0xad, 0x01, 0x0a, 0x25, 0x43, 0x72, 0x65,\n\t0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,\n\t0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x69, 0x74, 0x46, 0x6c,\n\t0x6f, 0x77, 0x12, 0x42, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f,\n\t0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74,\n\t0x69, 0x61, 0x6c, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53,\n\t0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x69, 0x74, 0x46, 0x6c, 0x6f, 0x77, 0x2e, 0x52,\n\t0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x40, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74,\n\t0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65,\n\t0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,\n\t0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x69, 0x74, 0x46, 0x6c,\n\t0x6f, 0x77, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0xb9, 0x01, 0x0a, 0x29, 0x43, 0x72, 0x65,\n\t0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,\n\t0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65,\n\t0x74, 0x65, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x46, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74,\n\t0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65,\n\t0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,\n\t0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65,\n\t0x74, 0x65, 0x46, 0x6c, 0x6f, 0x77, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x44,\n\t0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f,\n\t0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x56,\n\t0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69,\n\t0x63, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x46, 0x6c, 0x6f, 0x77, 0x2e, 0x52,\n\t0x65, 0x70, 0x6c, 0x79, 0x12, 0x85, 0x01, 0x0a, 0x17, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65,\n\t0x64, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x4c, 0x69, 0x73, 0x74,\n\t0x12, 0x34, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,\n\t0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x43,\n\t0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x2e, 0x52,\n\t0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74,\n\t0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x65, 0x72,\n\t0x69, 0x66, 0x69, 0x65, 0x64, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73,\n\t0x4c, 0x69, 0x73, 0x74, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x30, 0x01, 0x12, 0x9b, 0x01, 0x0a,\n\t0x1f, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76,\n\t0x69, 0x63, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70,\n\t0x12, 0x3c, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,\n\t0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69,\n\t0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65,\n\t0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3a,\n\t0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f,\n\t0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,\n\t0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x47,\n\t0x72, 0x6f, 0x75, 0x70, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x56, 0x0a, 0x08, 0x50, 0x65,\n\t0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x25, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74,\n\t0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x65, 0x65,\n\t0x72, 0x4c, 0x69, 0x73, 0x74, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e,\n\t0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c,\n\t0x2e, 0x76, 0x31, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x2e, 0x52, 0x65, 0x70,\n\t0x6c, 0x79, 0x12, 0x71, 0x0a, 0x11, 0x4f, 0x75, 0x74, 0x4f, 0x66, 0x53, 0x74, 0x6f, 0x72, 0x65,\n\t0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x12, 0x2e, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65,\n\t0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75,\n\t0x74, 0x4f, 0x66, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x2e,\n\t0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65,\n\t0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75,\n\t0x74, 0x4f, 0x66, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x2e,\n\t0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x68, 0x0a, 0x0e, 0x4f, 0x75, 0x74, 0x4f, 0x66, 0x53, 0x74,\n\t0x6f, 0x72, 0x65, 0x53, 0x65, 0x61, 0x6c, 0x12, 0x2b, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65,\n\t0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75,\n\t0x74, 0x4f, 0x66, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x53, 0x65, 0x61, 0x6c, 0x2e, 0x52, 0x65, 0x71,\n\t0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70,\n\t0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x4f, 0x66,\n\t0x53, 0x74, 0x6f, 0x72, 0x65, 0x53, 0x65, 0x61, 0x6c, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12,\n\t0x7d, 0x0a, 0x15, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63,\n\t0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x32, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e,\n\t0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52,\n\t0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71,\n\t0x75, 0x65, 0x73, 0x74, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x77,\n\t0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e,\n\t0x76, 0x31, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63,\n\t0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x42, 0x29,\n\t0x5a, 0x27, 0x62, 0x65, 0x72, 0x74, 0x79, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x2f, 0x77, 0x65, 0x73,\n\t0x68, 0x6e, 0x65, 0x74, 0x2f, 0x76, 0x32, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74,\n\t0x6f, 0x63, 0x6f, 0x6c, 0x74, 0x79, 0x70, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,\n\t0x33,\n}\n\nvar (\n\tfile_protocoltypes_proto_rawDescOnce sync.Once\n\tfile_protocoltypes_proto_rawDescData = file_protocoltypes_proto_rawDesc\n)\n\nfunc file_protocoltypes_proto_rawDescGZIP() []byte {\n\tfile_protocoltypes_proto_rawDescOnce.Do(func() {\n\t\tfile_protocoltypes_proto_rawDescData = protoimpl.X.CompressGZIP(file_protocoltypes_proto_rawDescData)\n\t})\n\treturn file_protocoltypes_proto_rawDescData\n}\n\nvar file_protocoltypes_proto_enumTypes = make([]protoimpl.EnumInfo, 9)\nvar file_protocoltypes_proto_msgTypes = make([]protoimpl.MessageInfo, 178)\nvar file_protocoltypes_proto_goTypes = []any{\n\t(GroupType)(0),                                            // 0: weshnet.protocol.v1.GroupType\n\t(EventType)(0),                                            // 1: weshnet.protocol.v1.EventType\n\t(DebugInspectGroupLogType)(0),                             // 2: weshnet.protocol.v1.DebugInspectGroupLogType\n\t(ContactState)(0),                                         // 3: weshnet.protocol.v1.ContactState\n\t(Direction)(0),                                            // 4: weshnet.protocol.v1.Direction\n\t(ServiceGetConfiguration_SettingState)(0),                 // 5: weshnet.protocol.v1.ServiceGetConfiguration.SettingState\n\t(GroupDeviceStatus_Type)(0),                               // 6: weshnet.protocol.v1.GroupDeviceStatus.Type\n\t(GroupDeviceStatus_Transport)(0),                          // 7: weshnet.protocol.v1.GroupDeviceStatus.Transport\n\t(PeerList_Feature)(0),                                     // 8: weshnet.protocol.v1.PeerList.Feature\n\t(*Account)(nil),                                           // 9: weshnet.protocol.v1.Account\n\t(*Group)(nil),                                             // 10: weshnet.protocol.v1.Group\n\t(*GroupHeadsExport)(nil),                                  // 11: weshnet.protocol.v1.GroupHeadsExport\n\t(*GroupMetadata)(nil),                                     // 12: weshnet.protocol.v1.GroupMetadata\n\t(*GroupEnvelope)(nil),                                     // 13: weshnet.protocol.v1.GroupEnvelope\n\t(*MessageHeaders)(nil),                                    // 14: weshnet.protocol.v1.MessageHeaders\n\t(*ProtocolMetadata)(nil),                                  // 15: weshnet.protocol.v1.ProtocolMetadata\n\t(*EncryptedMessage)(nil),                                  // 16: weshnet.protocol.v1.EncryptedMessage\n\t(*MessageEnvelope)(nil),                                   // 17: weshnet.protocol.v1.MessageEnvelope\n\t(*EventContext)(nil),                                      // 18: weshnet.protocol.v1.EventContext\n\t(*GroupMetadataPayloadSent)(nil),                          // 19: weshnet.protocol.v1.GroupMetadataPayloadSent\n\t(*ContactAliasKeyAdded)(nil),                              // 20: weshnet.protocol.v1.ContactAliasKeyAdded\n\t(*GroupMemberDeviceAdded)(nil),                            // 21: weshnet.protocol.v1.GroupMemberDeviceAdded\n\t(*DeviceChainKey)(nil),                                    // 22: weshnet.protocol.v1.DeviceChainKey\n\t(*GroupDeviceChainKeyAdded)(nil),                          // 23: weshnet.protocol.v1.GroupDeviceChainKeyAdded\n\t(*MultiMemberGroupAliasResolverAdded)(nil),                // 24: weshnet.protocol.v1.MultiMemberGroupAliasResolverAdded\n\t(*MultiMemberGroupAdminRoleGranted)(nil),                  // 25: weshnet.protocol.v1.MultiMemberGroupAdminRoleGranted\n\t(*MultiMemberGroupInitialMemberAnnounced)(nil),            // 26: weshnet.protocol.v1.MultiMemberGroupInitialMemberAnnounced\n\t(*GroupAddAdditionalRendezvousSeed)(nil),                  // 27: weshnet.protocol.v1.GroupAddAdditionalRendezvousSeed\n\t(*GroupRemoveAdditionalRendezvousSeed)(nil),               // 28: weshnet.protocol.v1.GroupRemoveAdditionalRendezvousSeed\n\t(*AccountGroupJoined)(nil),                                // 29: weshnet.protocol.v1.AccountGroupJoined\n\t(*AccountGroupLeft)(nil),                                  // 30: weshnet.protocol.v1.AccountGroupLeft\n\t(*AccountContactRequestDisabled)(nil),                     // 31: weshnet.protocol.v1.AccountContactRequestDisabled\n\t(*AccountContactRequestEnabled)(nil),                      // 32: weshnet.protocol.v1.AccountContactRequestEnabled\n\t(*AccountContactRequestReferenceReset)(nil),               // 33: weshnet.protocol.v1.AccountContactRequestReferenceReset\n\t(*AccountContactRequestOutgoingEnqueued)(nil),             // 34: weshnet.protocol.v1.AccountContactRequestOutgoingEnqueued\n\t(*AccountContactRequestOutgoingSent)(nil),                 // 35: weshnet.protocol.v1.AccountContactRequestOutgoingSent\n\t(*AccountContactRequestIncomingReceived)(nil),             // 36: weshnet.protocol.v1.AccountContactRequestIncomingReceived\n\t(*AccountContactRequestIncomingDiscarded)(nil),            // 37: weshnet.protocol.v1.AccountContactRequestIncomingDiscarded\n\t(*AccountContactRequestIncomingAccepted)(nil),             // 38: weshnet.protocol.v1.AccountContactRequestIncomingAccepted\n\t(*AccountContactBlocked)(nil),                             // 39: weshnet.protocol.v1.AccountContactBlocked\n\t(*AccountContactUnblocked)(nil),                           // 40: weshnet.protocol.v1.AccountContactUnblocked\n\t(*GroupReplicating)(nil),                                  // 41: weshnet.protocol.v1.GroupReplicating\n\t(*ServiceExportData)(nil),                                 // 42: weshnet.protocol.v1.ServiceExportData\n\t(*ServiceGetConfiguration)(nil),                           // 43: weshnet.protocol.v1.ServiceGetConfiguration\n\t(*ContactRequestReference)(nil),                           // 44: weshnet.protocol.v1.ContactRequestReference\n\t(*ContactRequestDisable)(nil),                             // 45: weshnet.protocol.v1.ContactRequestDisable\n\t(*ContactRequestEnable)(nil),                              // 46: weshnet.protocol.v1.ContactRequestEnable\n\t(*ContactRequestResetReference)(nil),                      // 47: weshnet.protocol.v1.ContactRequestResetReference\n\t(*ContactRequestSend)(nil),                                // 48: weshnet.protocol.v1.ContactRequestSend\n\t(*ContactRequestAccept)(nil),                              // 49: weshnet.protocol.v1.ContactRequestAccept\n\t(*ContactRequestDiscard)(nil),                             // 50: weshnet.protocol.v1.ContactRequestDiscard\n\t(*ShareContact)(nil),                                      // 51: weshnet.protocol.v1.ShareContact\n\t(*DecodeContact)(nil),                                     // 52: weshnet.protocol.v1.DecodeContact\n\t(*ContactBlock)(nil),                                      // 53: weshnet.protocol.v1.ContactBlock\n\t(*ContactUnblock)(nil),                                    // 54: weshnet.protocol.v1.ContactUnblock\n\t(*ContactAliasKeySend)(nil),                               // 55: weshnet.protocol.v1.ContactAliasKeySend\n\t(*MultiMemberGroupCreate)(nil),                            // 56: weshnet.protocol.v1.MultiMemberGroupCreate\n\t(*MultiMemberGroupJoin)(nil),                              // 57: weshnet.protocol.v1.MultiMemberGroupJoin\n\t(*MultiMemberGroupLeave)(nil),                             // 58: weshnet.protocol.v1.MultiMemberGroupLeave\n\t(*MultiMemberGroupAliasResolverDisclose)(nil),             // 59: weshnet.protocol.v1.MultiMemberGroupAliasResolverDisclose\n\t(*MultiMemberGroupAdminRoleGrant)(nil),                    // 60: weshnet.protocol.v1.MultiMemberGroupAdminRoleGrant\n\t(*MultiMemberGroupInvitationCreate)(nil),                  // 61: weshnet.protocol.v1.MultiMemberGroupInvitationCreate\n\t(*AppMetadataSend)(nil),                                   // 62: weshnet.protocol.v1.AppMetadataSend\n\t(*AppMessageSend)(nil),                                    // 63: weshnet.protocol.v1.AppMessageSend\n\t(*GroupMetadataEvent)(nil),                                // 64: weshnet.protocol.v1.GroupMetadataEvent\n\t(*GroupMessageEvent)(nil),                                 // 65: weshnet.protocol.v1.GroupMessageEvent\n\t(*GroupMetadataList)(nil),                                 // 66: weshnet.protocol.v1.GroupMetadataList\n\t(*GroupMessageList)(nil),                                  // 67: weshnet.protocol.v1.GroupMessageList\n\t(*GroupInfo)(nil),                                         // 68: weshnet.protocol.v1.GroupInfo\n\t(*ActivateGroup)(nil),                                     // 69: weshnet.protocol.v1.ActivateGroup\n\t(*DeactivateGroup)(nil),                                   // 70: weshnet.protocol.v1.DeactivateGroup\n\t(*GroupDeviceStatus)(nil),                                 // 71: weshnet.protocol.v1.GroupDeviceStatus\n\t(*DebugListGroups)(nil),                                   // 72: weshnet.protocol.v1.DebugListGroups\n\t(*DebugInspectGroupStore)(nil),                            // 73: weshnet.protocol.v1.DebugInspectGroupStore\n\t(*DebugGroup)(nil),                                        // 74: weshnet.protocol.v1.DebugGroup\n\t(*ShareableContact)(nil),                                  // 75: weshnet.protocol.v1.ShareableContact\n\t(*ServiceTokenSupportedService)(nil),                      // 76: weshnet.protocol.v1.ServiceTokenSupportedService\n\t(*ServiceToken)(nil),                                      // 77: weshnet.protocol.v1.ServiceToken\n\t(*CredentialVerificationServiceInitFlow)(nil),             // 78: weshnet.protocol.v1.CredentialVerificationServiceInitFlow\n\t(*CredentialVerificationServiceCompleteFlow)(nil),         // 79: weshnet.protocol.v1.CredentialVerificationServiceCompleteFlow\n\t(*VerifiedCredentialsList)(nil),                           // 80: weshnet.protocol.v1.VerifiedCredentialsList\n\t(*ReplicationServiceRegisterGroup)(nil),                   // 81: weshnet.protocol.v1.ReplicationServiceRegisterGroup\n\t(*ReplicationServiceReplicateGroup)(nil),                  // 82: weshnet.protocol.v1.ReplicationServiceReplicateGroup\n\t(*SystemInfo)(nil),                                        // 83: weshnet.protocol.v1.SystemInfo\n\t(*PeerList)(nil),                                          // 84: weshnet.protocol.v1.PeerList\n\t(*Progress)(nil),                                          // 85: weshnet.protocol.v1.Progress\n\t(*OutOfStoreMessage)(nil),                                 // 86: weshnet.protocol.v1.OutOfStoreMessage\n\t(*OutOfStoreMessageEnvelope)(nil),                         // 87: weshnet.protocol.v1.OutOfStoreMessageEnvelope\n\t(*OutOfStoreReceive)(nil),                                 // 88: weshnet.protocol.v1.OutOfStoreReceive\n\t(*OutOfStoreSeal)(nil),                                    // 89: weshnet.protocol.v1.OutOfStoreSeal\n\t(*AccountVerifiedCredentialRegistered)(nil),               // 90: weshnet.protocol.v1.AccountVerifiedCredentialRegistered\n\t(*FirstLastCounters)(nil),                                 // 91: weshnet.protocol.v1.FirstLastCounters\n\t(*OrbitDBMessageHeads)(nil),                               // 92: weshnet.protocol.v1.OrbitDBMessageHeads\n\t(*RefreshContactRequest)(nil),                             // 93: weshnet.protocol.v1.RefreshContactRequest\n\tnil,                                                       // 94: weshnet.protocol.v1.MessageHeaders.MetadataEntry\n\t(*ServiceExportData_Request)(nil),                         // 95: weshnet.protocol.v1.ServiceExportData.Request\n\t(*ServiceExportData_Reply)(nil),                           // 96: weshnet.protocol.v1.ServiceExportData.Reply\n\t(*ServiceGetConfiguration_Request)(nil),                   // 97: weshnet.protocol.v1.ServiceGetConfiguration.Request\n\t(*ServiceGetConfiguration_Reply)(nil),                     // 98: weshnet.protocol.v1.ServiceGetConfiguration.Reply\n\t(*ContactRequestReference_Request)(nil),                   // 99: weshnet.protocol.v1.ContactRequestReference.Request\n\t(*ContactRequestReference_Reply)(nil),                     // 100: weshnet.protocol.v1.ContactRequestReference.Reply\n\t(*ContactRequestDisable_Request)(nil),                     // 101: weshnet.protocol.v1.ContactRequestDisable.Request\n\t(*ContactRequestDisable_Reply)(nil),                       // 102: weshnet.protocol.v1.ContactRequestDisable.Reply\n\t(*ContactRequestEnable_Request)(nil),                      // 103: weshnet.protocol.v1.ContactRequestEnable.Request\n\t(*ContactRequestEnable_Reply)(nil),                        // 104: weshnet.protocol.v1.ContactRequestEnable.Reply\n\t(*ContactRequestResetReference_Request)(nil),              // 105: weshnet.protocol.v1.ContactRequestResetReference.Request\n\t(*ContactRequestResetReference_Reply)(nil),                // 106: weshnet.protocol.v1.ContactRequestResetReference.Reply\n\t(*ContactRequestSend_Request)(nil),                        // 107: weshnet.protocol.v1.ContactRequestSend.Request\n\t(*ContactRequestSend_Reply)(nil),                          // 108: weshnet.protocol.v1.ContactRequestSend.Reply\n\t(*ContactRequestAccept_Request)(nil),                      // 109: weshnet.protocol.v1.ContactRequestAccept.Request\n\t(*ContactRequestAccept_Reply)(nil),                        // 110: weshnet.protocol.v1.ContactRequestAccept.Reply\n\t(*ContactRequestDiscard_Request)(nil),                     // 111: weshnet.protocol.v1.ContactRequestDiscard.Request\n\t(*ContactRequestDiscard_Reply)(nil),                       // 112: weshnet.protocol.v1.ContactRequestDiscard.Reply\n\t(*ShareContact_Request)(nil),                              // 113: weshnet.protocol.v1.ShareContact.Request\n\t(*ShareContact_Reply)(nil),                                // 114: weshnet.protocol.v1.ShareContact.Reply\n\t(*DecodeContact_Request)(nil),                             // 115: weshnet.protocol.v1.DecodeContact.Request\n\t(*DecodeContact_Reply)(nil),                               // 116: weshnet.protocol.v1.DecodeContact.Reply\n\t(*ContactBlock_Request)(nil),                              // 117: weshnet.protocol.v1.ContactBlock.Request\n\t(*ContactBlock_Reply)(nil),                                // 118: weshnet.protocol.v1.ContactBlock.Reply\n\t(*ContactUnblock_Request)(nil),                            // 119: weshnet.protocol.v1.ContactUnblock.Request\n\t(*ContactUnblock_Reply)(nil),                              // 120: weshnet.protocol.v1.ContactUnblock.Reply\n\t(*ContactAliasKeySend_Request)(nil),                       // 121: weshnet.protocol.v1.ContactAliasKeySend.Request\n\t(*ContactAliasKeySend_Reply)(nil),                         // 122: weshnet.protocol.v1.ContactAliasKeySend.Reply\n\t(*MultiMemberGroupCreate_Request)(nil),                    // 123: weshnet.protocol.v1.MultiMemberGroupCreate.Request\n\t(*MultiMemberGroupCreate_Reply)(nil),                      // 124: weshnet.protocol.v1.MultiMemberGroupCreate.Reply\n\t(*MultiMemberGroupJoin_Request)(nil),                      // 125: weshnet.protocol.v1.MultiMemberGroupJoin.Request\n\t(*MultiMemberGroupJoin_Reply)(nil),                        // 126: weshnet.protocol.v1.MultiMemberGroupJoin.Reply\n\t(*MultiMemberGroupLeave_Request)(nil),                     // 127: weshnet.protocol.v1.MultiMemberGroupLeave.Request\n\t(*MultiMemberGroupLeave_Reply)(nil),                       // 128: weshnet.protocol.v1.MultiMemberGroupLeave.Reply\n\t(*MultiMemberGroupAliasResolverDisclose_Request)(nil),     // 129: weshnet.protocol.v1.MultiMemberGroupAliasResolverDisclose.Request\n\t(*MultiMemberGroupAliasResolverDisclose_Reply)(nil),       // 130: weshnet.protocol.v1.MultiMemberGroupAliasResolverDisclose.Reply\n\t(*MultiMemberGroupAdminRoleGrant_Request)(nil),            // 131: weshnet.protocol.v1.MultiMemberGroupAdminRoleGrant.Request\n\t(*MultiMemberGroupAdminRoleGrant_Reply)(nil),              // 132: weshnet.protocol.v1.MultiMemberGroupAdminRoleGrant.Reply\n\t(*MultiMemberGroupInvitationCreate_Request)(nil),          // 133: weshnet.protocol.v1.MultiMemberGroupInvitationCreate.Request\n\t(*MultiMemberGroupInvitationCreate_Reply)(nil),            // 134: weshnet.protocol.v1.MultiMemberGroupInvitationCreate.Reply\n\t(*AppMetadataSend_Request)(nil),                           // 135: weshnet.protocol.v1.AppMetadataSend.Request\n\t(*AppMetadataSend_Reply)(nil),                             // 136: weshnet.protocol.v1.AppMetadataSend.Reply\n\t(*AppMessageSend_Request)(nil),                            // 137: weshnet.protocol.v1.AppMessageSend.Request\n\t(*AppMessageSend_Reply)(nil),                              // 138: weshnet.protocol.v1.AppMessageSend.Reply\n\t(*GroupMetadataList_Request)(nil),                         // 139: weshnet.protocol.v1.GroupMetadataList.Request\n\t(*GroupMessageList_Request)(nil),                          // 140: weshnet.protocol.v1.GroupMessageList.Request\n\t(*GroupInfo_Request)(nil),                                 // 141: weshnet.protocol.v1.GroupInfo.Request\n\t(*GroupInfo_Reply)(nil),                                   // 142: weshnet.protocol.v1.GroupInfo.Reply\n\t(*ActivateGroup_Request)(nil),                             // 143: weshnet.protocol.v1.ActivateGroup.Request\n\t(*ActivateGroup_Reply)(nil),                               // 144: weshnet.protocol.v1.ActivateGroup.Reply\n\t(*DeactivateGroup_Request)(nil),                           // 145: weshnet.protocol.v1.DeactivateGroup.Request\n\t(*DeactivateGroup_Reply)(nil),                             // 146: weshnet.protocol.v1.DeactivateGroup.Reply\n\t(*GroupDeviceStatus_Request)(nil),                         // 147: weshnet.protocol.v1.GroupDeviceStatus.Request\n\t(*GroupDeviceStatus_Reply)(nil),                           // 148: weshnet.protocol.v1.GroupDeviceStatus.Reply\n\t(*GroupDeviceStatus_Reply_PeerConnected)(nil),             // 149: weshnet.protocol.v1.GroupDeviceStatus.Reply.PeerConnected\n\t(*GroupDeviceStatus_Reply_PeerReconnecting)(nil),          // 150: weshnet.protocol.v1.GroupDeviceStatus.Reply.PeerReconnecting\n\t(*GroupDeviceStatus_Reply_PeerDisconnected)(nil),          // 151: weshnet.protocol.v1.GroupDeviceStatus.Reply.PeerDisconnected\n\t(*DebugListGroups_Request)(nil),                           // 152: weshnet.protocol.v1.DebugListGroups.Request\n\t(*DebugListGroups_Reply)(nil),                             // 153: weshnet.protocol.v1.DebugListGroups.Reply\n\t(*DebugInspectGroupStore_Request)(nil),                    // 154: weshnet.protocol.v1.DebugInspectGroupStore.Request\n\t(*DebugInspectGroupStore_Reply)(nil),                      // 155: weshnet.protocol.v1.DebugInspectGroupStore.Reply\n\t(*DebugGroup_Request)(nil),                                // 156: weshnet.protocol.v1.DebugGroup.Request\n\t(*DebugGroup_Reply)(nil),                                  // 157: weshnet.protocol.v1.DebugGroup.Reply\n\t(*CredentialVerificationServiceInitFlow_Request)(nil),     // 158: weshnet.protocol.v1.CredentialVerificationServiceInitFlow.Request\n\t(*CredentialVerificationServiceInitFlow_Reply)(nil),       // 159: weshnet.protocol.v1.CredentialVerificationServiceInitFlow.Reply\n\t(*CredentialVerificationServiceCompleteFlow_Request)(nil), // 160: weshnet.protocol.v1.CredentialVerificationServiceCompleteFlow.Request\n\t(*CredentialVerificationServiceCompleteFlow_Reply)(nil),   // 161: weshnet.protocol.v1.CredentialVerificationServiceCompleteFlow.Reply\n\t(*VerifiedCredentialsList_Request)(nil),                   // 162: weshnet.protocol.v1.VerifiedCredentialsList.Request\n\t(*VerifiedCredentialsList_Reply)(nil),                     // 163: weshnet.protocol.v1.VerifiedCredentialsList.Reply\n\t(*ReplicationServiceRegisterGroup_Request)(nil),           // 164: weshnet.protocol.v1.ReplicationServiceRegisterGroup.Request\n\t(*ReplicationServiceRegisterGroup_Reply)(nil),             // 165: weshnet.protocol.v1.ReplicationServiceRegisterGroup.Reply\n\t(*ReplicationServiceReplicateGroup_Request)(nil),          // 166: weshnet.protocol.v1.ReplicationServiceReplicateGroup.Request\n\t(*ReplicationServiceReplicateGroup_Reply)(nil),            // 167: weshnet.protocol.v1.ReplicationServiceReplicateGroup.Reply\n\t(*SystemInfo_Request)(nil),                                // 168: weshnet.protocol.v1.SystemInfo.Request\n\t(*SystemInfo_Reply)(nil),                                  // 169: weshnet.protocol.v1.SystemInfo.Reply\n\t(*SystemInfo_OrbitDB)(nil),                                // 170: weshnet.protocol.v1.SystemInfo.OrbitDB\n\t(*SystemInfo_P2P)(nil),                                    // 171: weshnet.protocol.v1.SystemInfo.P2P\n\t(*SystemInfo_Process)(nil),                                // 172: weshnet.protocol.v1.SystemInfo.Process\n\t(*SystemInfo_OrbitDB_ReplicationStatus)(nil),              // 173: weshnet.protocol.v1.SystemInfo.OrbitDB.ReplicationStatus\n\t(*PeerList_Request)(nil),                                  // 174: weshnet.protocol.v1.PeerList.Request\n\t(*PeerList_Reply)(nil),                                    // 175: weshnet.protocol.v1.PeerList.Reply\n\t(*PeerList_Peer)(nil),                                     // 176: weshnet.protocol.v1.PeerList.Peer\n\t(*PeerList_Route)(nil),                                    // 177: weshnet.protocol.v1.PeerList.Route\n\t(*PeerList_Stream)(nil),                                   // 178: weshnet.protocol.v1.PeerList.Stream\n\t(*OutOfStoreReceive_Request)(nil),                         // 179: weshnet.protocol.v1.OutOfStoreReceive.Request\n\t(*OutOfStoreReceive_Reply)(nil),                           // 180: weshnet.protocol.v1.OutOfStoreReceive.Reply\n\t(*OutOfStoreSeal_Request)(nil),                            // 181: weshnet.protocol.v1.OutOfStoreSeal.Request\n\t(*OutOfStoreSeal_Reply)(nil),                              // 182: weshnet.protocol.v1.OutOfStoreSeal.Reply\n\t(*OrbitDBMessageHeads_Box)(nil),                           // 183: weshnet.protocol.v1.OrbitDBMessageHeads.Box\n\t(*RefreshContactRequest_Peer)(nil),                        // 184: weshnet.protocol.v1.RefreshContactRequest.Peer\n\t(*RefreshContactRequest_Request)(nil),                     // 185: weshnet.protocol.v1.RefreshContactRequest.Request\n\t(*RefreshContactRequest_Reply)(nil),                       // 186: weshnet.protocol.v1.RefreshContactRequest.Reply\n}\nvar file_protocoltypes_proto_depIdxs = []int32{\n\t10,  // 0: weshnet.protocol.v1.Account.group:type_name -> weshnet.protocol.v1.Group\n\t0,   // 1: weshnet.protocol.v1.Group.group_type:type_name -> weshnet.protocol.v1.GroupType\n\t1,   // 2: weshnet.protocol.v1.GroupMetadata.event_type:type_name -> weshnet.protocol.v1.EventType\n\t15,  // 3: weshnet.protocol.v1.GroupMetadata.protocol_metadata:type_name -> weshnet.protocol.v1.ProtocolMetadata\n\t94,  // 4: weshnet.protocol.v1.MessageHeaders.metadata:type_name -> weshnet.protocol.v1.MessageHeaders.MetadataEntry\n\t15,  // 5: weshnet.protocol.v1.EncryptedMessage.protocol_metadata:type_name -> weshnet.protocol.v1.ProtocolMetadata\n\t10,  // 6: weshnet.protocol.v1.AccountGroupJoined.group:type_name -> weshnet.protocol.v1.Group\n\t75,  // 7: weshnet.protocol.v1.AccountContactRequestOutgoingEnqueued.contact:type_name -> weshnet.protocol.v1.ShareableContact\n\t18,  // 8: weshnet.protocol.v1.GroupMetadataEvent.event_context:type_name -> weshnet.protocol.v1.EventContext\n\t12,  // 9: weshnet.protocol.v1.GroupMetadataEvent.metadata:type_name -> weshnet.protocol.v1.GroupMetadata\n\t18,  // 10: weshnet.protocol.v1.GroupMessageEvent.event_context:type_name -> weshnet.protocol.v1.EventContext\n\t14,  // 11: weshnet.protocol.v1.GroupMessageEvent.headers:type_name -> weshnet.protocol.v1.MessageHeaders\n\t76,  // 12: weshnet.protocol.v1.ServiceToken.supported_services:type_name -> weshnet.protocol.v1.ServiceTokenSupportedService\n\t5,   // 13: weshnet.protocol.v1.ServiceGetConfiguration.Reply.ble_enabled:type_name -> weshnet.protocol.v1.ServiceGetConfiguration.SettingState\n\t5,   // 14: weshnet.protocol.v1.ServiceGetConfiguration.Reply.wifi_p2p_enabled:type_name -> weshnet.protocol.v1.ServiceGetConfiguration.SettingState\n\t5,   // 15: weshnet.protocol.v1.ServiceGetConfiguration.Reply.mdns_enabled:type_name -> weshnet.protocol.v1.ServiceGetConfiguration.SettingState\n\t5,   // 16: weshnet.protocol.v1.ServiceGetConfiguration.Reply.relay_enabled:type_name -> weshnet.protocol.v1.ServiceGetConfiguration.SettingState\n\t75,  // 17: weshnet.protocol.v1.ContactRequestSend.Request.contact:type_name -> weshnet.protocol.v1.ShareableContact\n\t75,  // 18: weshnet.protocol.v1.DecodeContact.Reply.contact:type_name -> weshnet.protocol.v1.ShareableContact\n\t10,  // 19: weshnet.protocol.v1.MultiMemberGroupJoin.Request.group:type_name -> weshnet.protocol.v1.Group\n\t10,  // 20: weshnet.protocol.v1.MultiMemberGroupInvitationCreate.Reply.group:type_name -> weshnet.protocol.v1.Group\n\t10,  // 21: weshnet.protocol.v1.GroupInfo.Reply.group:type_name -> weshnet.protocol.v1.Group\n\t6,   // 22: weshnet.protocol.v1.GroupDeviceStatus.Reply.type:type_name -> weshnet.protocol.v1.GroupDeviceStatus.Type\n\t7,   // 23: weshnet.protocol.v1.GroupDeviceStatus.Reply.PeerConnected.transports:type_name -> weshnet.protocol.v1.GroupDeviceStatus.Transport\n\t0,   // 24: weshnet.protocol.v1.DebugListGroups.Reply.group_type:type_name -> weshnet.protocol.v1.GroupType\n\t2,   // 25: weshnet.protocol.v1.DebugInspectGroupStore.Request.log_type:type_name -> weshnet.protocol.v1.DebugInspectGroupLogType\n\t1,   // 26: weshnet.protocol.v1.DebugInspectGroupStore.Reply.metadata_event_type:type_name -> weshnet.protocol.v1.EventType\n\t90,  // 27: weshnet.protocol.v1.VerifiedCredentialsList.Reply.credential:type_name -> weshnet.protocol.v1.AccountVerifiedCredentialRegistered\n\t10,  // 28: weshnet.protocol.v1.ReplicationServiceReplicateGroup.Request.group:type_name -> weshnet.protocol.v1.Group\n\t172, // 29: weshnet.protocol.v1.SystemInfo.Reply.process:type_name -> weshnet.protocol.v1.SystemInfo.Process\n\t171, // 30: weshnet.protocol.v1.SystemInfo.Reply.p2p:type_name -> weshnet.protocol.v1.SystemInfo.P2P\n\t170, // 31: weshnet.protocol.v1.SystemInfo.Reply.orbitdb:type_name -> weshnet.protocol.v1.SystemInfo.OrbitDB\n\t173, // 32: weshnet.protocol.v1.SystemInfo.OrbitDB.account_metadata:type_name -> weshnet.protocol.v1.SystemInfo.OrbitDB.ReplicationStatus\n\t176, // 33: weshnet.protocol.v1.PeerList.Reply.peers:type_name -> weshnet.protocol.v1.PeerList.Peer\n\t177, // 34: weshnet.protocol.v1.PeerList.Peer.routes:type_name -> weshnet.protocol.v1.PeerList.Route\n\t8,   // 35: weshnet.protocol.v1.PeerList.Peer.features:type_name -> weshnet.protocol.v1.PeerList.Feature\n\t4,   // 36: weshnet.protocol.v1.PeerList.Peer.direction:type_name -> weshnet.protocol.v1.Direction\n\t4,   // 37: weshnet.protocol.v1.PeerList.Route.direction:type_name -> weshnet.protocol.v1.Direction\n\t178, // 38: weshnet.protocol.v1.PeerList.Route.streams:type_name -> weshnet.protocol.v1.PeerList.Stream\n\t86,  // 39: weshnet.protocol.v1.OutOfStoreReceive.Reply.message:type_name -> weshnet.protocol.v1.OutOfStoreMessage\n\t184, // 40: weshnet.protocol.v1.RefreshContactRequest.Reply.peers_found:type_name -> weshnet.protocol.v1.RefreshContactRequest.Peer\n\t95,  // 41: weshnet.protocol.v1.ProtocolService.ServiceExportData:input_type -> weshnet.protocol.v1.ServiceExportData.Request\n\t97,  // 42: weshnet.protocol.v1.ProtocolService.ServiceGetConfiguration:input_type -> weshnet.protocol.v1.ServiceGetConfiguration.Request\n\t99,  // 43: weshnet.protocol.v1.ProtocolService.ContactRequestReference:input_type -> weshnet.protocol.v1.ContactRequestReference.Request\n\t101, // 44: weshnet.protocol.v1.ProtocolService.ContactRequestDisable:input_type -> weshnet.protocol.v1.ContactRequestDisable.Request\n\t103, // 45: weshnet.protocol.v1.ProtocolService.ContactRequestEnable:input_type -> weshnet.protocol.v1.ContactRequestEnable.Request\n\t105, // 46: weshnet.protocol.v1.ProtocolService.ContactRequestResetReference:input_type -> weshnet.protocol.v1.ContactRequestResetReference.Request\n\t107, // 47: weshnet.protocol.v1.ProtocolService.ContactRequestSend:input_type -> weshnet.protocol.v1.ContactRequestSend.Request\n\t109, // 48: weshnet.protocol.v1.ProtocolService.ContactRequestAccept:input_type -> weshnet.protocol.v1.ContactRequestAccept.Request\n\t111, // 49: weshnet.protocol.v1.ProtocolService.ContactRequestDiscard:input_type -> weshnet.protocol.v1.ContactRequestDiscard.Request\n\t113, // 50: weshnet.protocol.v1.ProtocolService.ShareContact:input_type -> weshnet.protocol.v1.ShareContact.Request\n\t115, // 51: weshnet.protocol.v1.ProtocolService.DecodeContact:input_type -> weshnet.protocol.v1.DecodeContact.Request\n\t117, // 52: weshnet.protocol.v1.ProtocolService.ContactBlock:input_type -> weshnet.protocol.v1.ContactBlock.Request\n\t119, // 53: weshnet.protocol.v1.ProtocolService.ContactUnblock:input_type -> weshnet.protocol.v1.ContactUnblock.Request\n\t121, // 54: weshnet.protocol.v1.ProtocolService.ContactAliasKeySend:input_type -> weshnet.protocol.v1.ContactAliasKeySend.Request\n\t123, // 55: weshnet.protocol.v1.ProtocolService.MultiMemberGroupCreate:input_type -> weshnet.protocol.v1.MultiMemberGroupCreate.Request\n\t125, // 56: weshnet.protocol.v1.ProtocolService.MultiMemberGroupJoin:input_type -> weshnet.protocol.v1.MultiMemberGroupJoin.Request\n\t127, // 57: weshnet.protocol.v1.ProtocolService.MultiMemberGroupLeave:input_type -> weshnet.protocol.v1.MultiMemberGroupLeave.Request\n\t129, // 58: weshnet.protocol.v1.ProtocolService.MultiMemberGroupAliasResolverDisclose:input_type -> weshnet.protocol.v1.MultiMemberGroupAliasResolverDisclose.Request\n\t131, // 59: weshnet.protocol.v1.ProtocolService.MultiMemberGroupAdminRoleGrant:input_type -> weshnet.protocol.v1.MultiMemberGroupAdminRoleGrant.Request\n\t133, // 60: weshnet.protocol.v1.ProtocolService.MultiMemberGroupInvitationCreate:input_type -> weshnet.protocol.v1.MultiMemberGroupInvitationCreate.Request\n\t135, // 61: weshnet.protocol.v1.ProtocolService.AppMetadataSend:input_type -> weshnet.protocol.v1.AppMetadataSend.Request\n\t137, // 62: weshnet.protocol.v1.ProtocolService.AppMessageSend:input_type -> weshnet.protocol.v1.AppMessageSend.Request\n\t139, // 63: weshnet.protocol.v1.ProtocolService.GroupMetadataList:input_type -> weshnet.protocol.v1.GroupMetadataList.Request\n\t140, // 64: weshnet.protocol.v1.ProtocolService.GroupMessageList:input_type -> weshnet.protocol.v1.GroupMessageList.Request\n\t141, // 65: weshnet.protocol.v1.ProtocolService.GroupInfo:input_type -> weshnet.protocol.v1.GroupInfo.Request\n\t143, // 66: weshnet.protocol.v1.ProtocolService.ActivateGroup:input_type -> weshnet.protocol.v1.ActivateGroup.Request\n\t145, // 67: weshnet.protocol.v1.ProtocolService.DeactivateGroup:input_type -> weshnet.protocol.v1.DeactivateGroup.Request\n\t147, // 68: weshnet.protocol.v1.ProtocolService.GroupDeviceStatus:input_type -> weshnet.protocol.v1.GroupDeviceStatus.Request\n\t152, // 69: weshnet.protocol.v1.ProtocolService.DebugListGroups:input_type -> weshnet.protocol.v1.DebugListGroups.Request\n\t154, // 70: weshnet.protocol.v1.ProtocolService.DebugInspectGroupStore:input_type -> weshnet.protocol.v1.DebugInspectGroupStore.Request\n\t156, // 71: weshnet.protocol.v1.ProtocolService.DebugGroup:input_type -> weshnet.protocol.v1.DebugGroup.Request\n\t168, // 72: weshnet.protocol.v1.ProtocolService.SystemInfo:input_type -> weshnet.protocol.v1.SystemInfo.Request\n\t158, // 73: weshnet.protocol.v1.ProtocolService.CredentialVerificationServiceInitFlow:input_type -> weshnet.protocol.v1.CredentialVerificationServiceInitFlow.Request\n\t160, // 74: weshnet.protocol.v1.ProtocolService.CredentialVerificationServiceCompleteFlow:input_type -> weshnet.protocol.v1.CredentialVerificationServiceCompleteFlow.Request\n\t162, // 75: weshnet.protocol.v1.ProtocolService.VerifiedCredentialsList:input_type -> weshnet.protocol.v1.VerifiedCredentialsList.Request\n\t164, // 76: weshnet.protocol.v1.ProtocolService.ReplicationServiceRegisterGroup:input_type -> weshnet.protocol.v1.ReplicationServiceRegisterGroup.Request\n\t174, // 77: weshnet.protocol.v1.ProtocolService.PeerList:input_type -> weshnet.protocol.v1.PeerList.Request\n\t179, // 78: weshnet.protocol.v1.ProtocolService.OutOfStoreReceive:input_type -> weshnet.protocol.v1.OutOfStoreReceive.Request\n\t181, // 79: weshnet.protocol.v1.ProtocolService.OutOfStoreSeal:input_type -> weshnet.protocol.v1.OutOfStoreSeal.Request\n\t185, // 80: weshnet.protocol.v1.ProtocolService.RefreshContactRequest:input_type -> weshnet.protocol.v1.RefreshContactRequest.Request\n\t96,  // 81: weshnet.protocol.v1.ProtocolService.ServiceExportData:output_type -> weshnet.protocol.v1.ServiceExportData.Reply\n\t98,  // 82: weshnet.protocol.v1.ProtocolService.ServiceGetConfiguration:output_type -> weshnet.protocol.v1.ServiceGetConfiguration.Reply\n\t100, // 83: weshnet.protocol.v1.ProtocolService.ContactRequestReference:output_type -> weshnet.protocol.v1.ContactRequestReference.Reply\n\t102, // 84: weshnet.protocol.v1.ProtocolService.ContactRequestDisable:output_type -> weshnet.protocol.v1.ContactRequestDisable.Reply\n\t104, // 85: weshnet.protocol.v1.ProtocolService.ContactRequestEnable:output_type -> weshnet.protocol.v1.ContactRequestEnable.Reply\n\t106, // 86: weshnet.protocol.v1.ProtocolService.ContactRequestResetReference:output_type -> weshnet.protocol.v1.ContactRequestResetReference.Reply\n\t108, // 87: weshnet.protocol.v1.ProtocolService.ContactRequestSend:output_type -> weshnet.protocol.v1.ContactRequestSend.Reply\n\t110, // 88: weshnet.protocol.v1.ProtocolService.ContactRequestAccept:output_type -> weshnet.protocol.v1.ContactRequestAccept.Reply\n\t112, // 89: weshnet.protocol.v1.ProtocolService.ContactRequestDiscard:output_type -> weshnet.protocol.v1.ContactRequestDiscard.Reply\n\t114, // 90: weshnet.protocol.v1.ProtocolService.ShareContact:output_type -> weshnet.protocol.v1.ShareContact.Reply\n\t116, // 91: weshnet.protocol.v1.ProtocolService.DecodeContact:output_type -> weshnet.protocol.v1.DecodeContact.Reply\n\t118, // 92: weshnet.protocol.v1.ProtocolService.ContactBlock:output_type -> weshnet.protocol.v1.ContactBlock.Reply\n\t120, // 93: weshnet.protocol.v1.ProtocolService.ContactUnblock:output_type -> weshnet.protocol.v1.ContactUnblock.Reply\n\t122, // 94: weshnet.protocol.v1.ProtocolService.ContactAliasKeySend:output_type -> weshnet.protocol.v1.ContactAliasKeySend.Reply\n\t124, // 95: weshnet.protocol.v1.ProtocolService.MultiMemberGroupCreate:output_type -> weshnet.protocol.v1.MultiMemberGroupCreate.Reply\n\t126, // 96: weshnet.protocol.v1.ProtocolService.MultiMemberGroupJoin:output_type -> weshnet.protocol.v1.MultiMemberGroupJoin.Reply\n\t128, // 97: weshnet.protocol.v1.ProtocolService.MultiMemberGroupLeave:output_type -> weshnet.protocol.v1.MultiMemberGroupLeave.Reply\n\t130, // 98: weshnet.protocol.v1.ProtocolService.MultiMemberGroupAliasResolverDisclose:output_type -> weshnet.protocol.v1.MultiMemberGroupAliasResolverDisclose.Reply\n\t132, // 99: weshnet.protocol.v1.ProtocolService.MultiMemberGroupAdminRoleGrant:output_type -> weshnet.protocol.v1.MultiMemberGroupAdminRoleGrant.Reply\n\t134, // 100: weshnet.protocol.v1.ProtocolService.MultiMemberGroupInvitationCreate:output_type -> weshnet.protocol.v1.MultiMemberGroupInvitationCreate.Reply\n\t136, // 101: weshnet.protocol.v1.ProtocolService.AppMetadataSend:output_type -> weshnet.protocol.v1.AppMetadataSend.Reply\n\t138, // 102: weshnet.protocol.v1.ProtocolService.AppMessageSend:output_type -> weshnet.protocol.v1.AppMessageSend.Reply\n\t64,  // 103: weshnet.protocol.v1.ProtocolService.GroupMetadataList:output_type -> weshnet.protocol.v1.GroupMetadataEvent\n\t65,  // 104: weshnet.protocol.v1.ProtocolService.GroupMessageList:output_type -> weshnet.protocol.v1.GroupMessageEvent\n\t142, // 105: weshnet.protocol.v1.ProtocolService.GroupInfo:output_type -> weshnet.protocol.v1.GroupInfo.Reply\n\t144, // 106: weshnet.protocol.v1.ProtocolService.ActivateGroup:output_type -> weshnet.protocol.v1.ActivateGroup.Reply\n\t146, // 107: weshnet.protocol.v1.ProtocolService.DeactivateGroup:output_type -> weshnet.protocol.v1.DeactivateGroup.Reply\n\t148, // 108: weshnet.protocol.v1.ProtocolService.GroupDeviceStatus:output_type -> weshnet.protocol.v1.GroupDeviceStatus.Reply\n\t153, // 109: weshnet.protocol.v1.ProtocolService.DebugListGroups:output_type -> weshnet.protocol.v1.DebugListGroups.Reply\n\t155, // 110: weshnet.protocol.v1.ProtocolService.DebugInspectGroupStore:output_type -> weshnet.protocol.v1.DebugInspectGroupStore.Reply\n\t157, // 111: weshnet.protocol.v1.ProtocolService.DebugGroup:output_type -> weshnet.protocol.v1.DebugGroup.Reply\n\t169, // 112: weshnet.protocol.v1.ProtocolService.SystemInfo:output_type -> weshnet.protocol.v1.SystemInfo.Reply\n\t159, // 113: weshnet.protocol.v1.ProtocolService.CredentialVerificationServiceInitFlow:output_type -> weshnet.protocol.v1.CredentialVerificationServiceInitFlow.Reply\n\t161, // 114: weshnet.protocol.v1.ProtocolService.CredentialVerificationServiceCompleteFlow:output_type -> weshnet.protocol.v1.CredentialVerificationServiceCompleteFlow.Reply\n\t163, // 115: weshnet.protocol.v1.ProtocolService.VerifiedCredentialsList:output_type -> weshnet.protocol.v1.VerifiedCredentialsList.Reply\n\t165, // 116: weshnet.protocol.v1.ProtocolService.ReplicationServiceRegisterGroup:output_type -> weshnet.protocol.v1.ReplicationServiceRegisterGroup.Reply\n\t175, // 117: weshnet.protocol.v1.ProtocolService.PeerList:output_type -> weshnet.protocol.v1.PeerList.Reply\n\t180, // 118: weshnet.protocol.v1.ProtocolService.OutOfStoreReceive:output_type -> weshnet.protocol.v1.OutOfStoreReceive.Reply\n\t182, // 119: weshnet.protocol.v1.ProtocolService.OutOfStoreSeal:output_type -> weshnet.protocol.v1.OutOfStoreSeal.Reply\n\t186, // 120: weshnet.protocol.v1.ProtocolService.RefreshContactRequest:output_type -> weshnet.protocol.v1.RefreshContactRequest.Reply\n\t81,  // [81:121] is the sub-list for method output_type\n\t41,  // [41:81] is the sub-list for method input_type\n\t41,  // [41:41] is the sub-list for extension type_name\n\t41,  // [41:41] is the sub-list for extension extendee\n\t0,   // [0:41] is the sub-list for field type_name\n}\n\nfunc init() { file_protocoltypes_proto_init() }\nfunc file_protocoltypes_proto_init() {\n\tif File_protocoltypes_proto != nil {\n\t\treturn\n\t}\n\tif !protoimpl.UnsafeEnabled {\n\t\tfile_protocoltypes_proto_msgTypes[0].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*Account); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[1].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*Group); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[2].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*GroupHeadsExport); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[3].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*GroupMetadata); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[4].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*GroupEnvelope); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[5].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*MessageHeaders); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[6].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ProtocolMetadata); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[7].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*EncryptedMessage); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[8].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*MessageEnvelope); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[9].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*EventContext); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[10].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*GroupMetadataPayloadSent); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[11].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ContactAliasKeyAdded); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[12].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*GroupMemberDeviceAdded); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[13].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*DeviceChainKey); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[14].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*GroupDeviceChainKeyAdded); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[15].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*MultiMemberGroupAliasResolverAdded); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[16].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*MultiMemberGroupAdminRoleGranted); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[17].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*MultiMemberGroupInitialMemberAnnounced); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[18].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*GroupAddAdditionalRendezvousSeed); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[19].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*GroupRemoveAdditionalRendezvousSeed); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[20].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*AccountGroupJoined); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[21].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*AccountGroupLeft); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[22].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*AccountContactRequestDisabled); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[23].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*AccountContactRequestEnabled); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[24].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*AccountContactRequestReferenceReset); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[25].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*AccountContactRequestOutgoingEnqueued); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[26].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*AccountContactRequestOutgoingSent); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[27].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*AccountContactRequestIncomingReceived); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[28].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*AccountContactRequestIncomingDiscarded); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[29].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*AccountContactRequestIncomingAccepted); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[30].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*AccountContactBlocked); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[31].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*AccountContactUnblocked); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[32].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*GroupReplicating); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[33].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ServiceExportData); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[34].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ServiceGetConfiguration); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[35].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ContactRequestReference); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[36].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ContactRequestDisable); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[37].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ContactRequestEnable); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[38].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ContactRequestResetReference); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[39].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ContactRequestSend); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[40].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ContactRequestAccept); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[41].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ContactRequestDiscard); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[42].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ShareContact); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[43].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*DecodeContact); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[44].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ContactBlock); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[45].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ContactUnblock); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[46].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ContactAliasKeySend); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[47].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*MultiMemberGroupCreate); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[48].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*MultiMemberGroupJoin); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[49].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*MultiMemberGroupLeave); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[50].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*MultiMemberGroupAliasResolverDisclose); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[51].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*MultiMemberGroupAdminRoleGrant); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[52].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*MultiMemberGroupInvitationCreate); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[53].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*AppMetadataSend); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[54].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*AppMessageSend); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[55].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*GroupMetadataEvent); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[56].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*GroupMessageEvent); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[57].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*GroupMetadataList); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[58].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*GroupMessageList); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[59].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*GroupInfo); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[60].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ActivateGroup); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[61].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*DeactivateGroup); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[62].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*GroupDeviceStatus); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[63].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*DebugListGroups); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[64].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*DebugInspectGroupStore); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[65].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*DebugGroup); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[66].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ShareableContact); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[67].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ServiceTokenSupportedService); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[68].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ServiceToken); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[69].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*CredentialVerificationServiceInitFlow); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[70].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*CredentialVerificationServiceCompleteFlow); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[71].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*VerifiedCredentialsList); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[72].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ReplicationServiceRegisterGroup); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[73].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ReplicationServiceReplicateGroup); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[74].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*SystemInfo); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[75].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*PeerList); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[76].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*Progress); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[77].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*OutOfStoreMessage); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[78].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*OutOfStoreMessageEnvelope); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[79].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*OutOfStoreReceive); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[80].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*OutOfStoreSeal); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[81].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*AccountVerifiedCredentialRegistered); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[82].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*FirstLastCounters); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[83].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*OrbitDBMessageHeads); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[84].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*RefreshContactRequest); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[86].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ServiceExportData_Request); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[87].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ServiceExportData_Reply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[88].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ServiceGetConfiguration_Request); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[89].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ServiceGetConfiguration_Reply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[90].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ContactRequestReference_Request); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[91].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ContactRequestReference_Reply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[92].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ContactRequestDisable_Request); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[93].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ContactRequestDisable_Reply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[94].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ContactRequestEnable_Request); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[95].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ContactRequestEnable_Reply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[96].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ContactRequestResetReference_Request); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[97].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ContactRequestResetReference_Reply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[98].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ContactRequestSend_Request); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[99].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ContactRequestSend_Reply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[100].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ContactRequestAccept_Request); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[101].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ContactRequestAccept_Reply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[102].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ContactRequestDiscard_Request); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[103].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ContactRequestDiscard_Reply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[104].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ShareContact_Request); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[105].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ShareContact_Reply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[106].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*DecodeContact_Request); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[107].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*DecodeContact_Reply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[108].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ContactBlock_Request); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[109].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ContactBlock_Reply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[110].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ContactUnblock_Request); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[111].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ContactUnblock_Reply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[112].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ContactAliasKeySend_Request); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[113].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ContactAliasKeySend_Reply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[114].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*MultiMemberGroupCreate_Request); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[115].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*MultiMemberGroupCreate_Reply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[116].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*MultiMemberGroupJoin_Request); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[117].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*MultiMemberGroupJoin_Reply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[118].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*MultiMemberGroupLeave_Request); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[119].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*MultiMemberGroupLeave_Reply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[120].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*MultiMemberGroupAliasResolverDisclose_Request); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[121].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*MultiMemberGroupAliasResolverDisclose_Reply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[122].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*MultiMemberGroupAdminRoleGrant_Request); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[123].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*MultiMemberGroupAdminRoleGrant_Reply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[124].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*MultiMemberGroupInvitationCreate_Request); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[125].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*MultiMemberGroupInvitationCreate_Reply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[126].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*AppMetadataSend_Request); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[127].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*AppMetadataSend_Reply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[128].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*AppMessageSend_Request); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[129].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*AppMessageSend_Reply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[130].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*GroupMetadataList_Request); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[131].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*GroupMessageList_Request); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[132].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*GroupInfo_Request); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[133].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*GroupInfo_Reply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[134].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ActivateGroup_Request); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[135].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ActivateGroup_Reply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[136].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*DeactivateGroup_Request); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[137].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*DeactivateGroup_Reply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[138].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*GroupDeviceStatus_Request); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[139].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*GroupDeviceStatus_Reply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[140].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*GroupDeviceStatus_Reply_PeerConnected); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[141].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*GroupDeviceStatus_Reply_PeerReconnecting); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[142].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*GroupDeviceStatus_Reply_PeerDisconnected); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[143].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*DebugListGroups_Request); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[144].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*DebugListGroups_Reply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[145].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*DebugInspectGroupStore_Request); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[146].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*DebugInspectGroupStore_Reply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[147].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*DebugGroup_Request); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[148].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*DebugGroup_Reply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[149].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*CredentialVerificationServiceInitFlow_Request); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[150].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*CredentialVerificationServiceInitFlow_Reply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[151].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*CredentialVerificationServiceCompleteFlow_Request); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[152].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*CredentialVerificationServiceCompleteFlow_Reply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[153].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*VerifiedCredentialsList_Request); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[154].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*VerifiedCredentialsList_Reply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[155].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ReplicationServiceRegisterGroup_Request); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[156].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ReplicationServiceRegisterGroup_Reply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[157].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ReplicationServiceReplicateGroup_Request); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[158].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ReplicationServiceReplicateGroup_Reply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[159].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*SystemInfo_Request); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[160].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*SystemInfo_Reply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[161].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*SystemInfo_OrbitDB); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[162].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*SystemInfo_P2P); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[163].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*SystemInfo_Process); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[164].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*SystemInfo_OrbitDB_ReplicationStatus); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[165].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*PeerList_Request); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[166].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*PeerList_Reply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[167].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*PeerList_Peer); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[168].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*PeerList_Route); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[169].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*PeerList_Stream); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[170].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*OutOfStoreReceive_Request); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[171].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*OutOfStoreReceive_Reply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[172].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*OutOfStoreSeal_Request); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[173].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*OutOfStoreSeal_Reply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[174].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*OrbitDBMessageHeads_Box); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[175].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*RefreshContactRequest_Peer); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[176].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*RefreshContactRequest_Request); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_protocoltypes_proto_msgTypes[177].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*RefreshContactRequest_Reply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n\ttype x struct{}\n\tout := protoimpl.TypeBuilder{\n\t\tFile: protoimpl.DescBuilder{\n\t\t\tGoPackagePath: reflect.TypeOf(x{}).PkgPath(),\n\t\t\tRawDescriptor: file_protocoltypes_proto_rawDesc,\n\t\t\tNumEnums:      9,\n\t\t\tNumMessages:   178,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   1,\n\t\t},\n\t\tGoTypes:           file_protocoltypes_proto_goTypes,\n\t\tDependencyIndexes: file_protocoltypes_proto_depIdxs,\n\t\tEnumInfos:         file_protocoltypes_proto_enumTypes,\n\t\tMessageInfos:      file_protocoltypes_proto_msgTypes,\n\t}.Build()\n\tFile_protocoltypes_proto = out.File\n\tfile_protocoltypes_proto_rawDesc = nil\n\tfile_protocoltypes_proto_goTypes = nil\n\tfile_protocoltypes_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "pkg/protocoltypes/protocoltypes.pb.gw.go",
    "content": "// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.\n// source: protocoltypes.proto\n\n/*\nPackage protocoltypes is a reverse proxy.\n\nIt translates gRPC into RESTful JSON APIs.\n*/\npackage protocoltypes\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"net/http\"\n\n\t\"github.com/golang/protobuf/descriptor\"\n\t\"github.com/golang/protobuf/proto\"\n\t\"github.com/grpc-ecosystem/grpc-gateway/runtime\"\n\t\"github.com/grpc-ecosystem/grpc-gateway/utilities\"\n\t\"google.golang.org/grpc\"\n\t\"google.golang.org/grpc/codes\"\n\t\"google.golang.org/grpc/grpclog\"\n\t\"google.golang.org/grpc/metadata\"\n\t\"google.golang.org/grpc/status\"\n)\n\n// Suppress \"imported and not used\" errors\nvar _ codes.Code\nvar _ io.Reader\nvar _ status.Status\nvar _ = runtime.String\nvar _ = utilities.NewDoubleArray\nvar _ = descriptor.ForMessage\nvar _ = metadata.Join\n\nfunc request_ProtocolService_ServiceExportData_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (ProtocolService_ServiceExportDataClient, runtime.ServerMetadata, error) {\n\tvar protoReq ServiceExportData_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tstream, err := client.ServiceExportData(ctx, &protoReq)\n\tif err != nil {\n\t\treturn nil, metadata, err\n\t}\n\theader, err := stream.Header()\n\tif err != nil {\n\t\treturn nil, metadata, err\n\t}\n\tmetadata.HeaderMD = header\n\treturn stream, metadata, nil\n\n}\n\nfunc request_ProtocolService_ServiceGetConfiguration_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq ServiceGetConfiguration_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := client.ServiceGetConfiguration(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))\n\treturn msg, metadata, err\n\n}\n\nfunc local_request_ProtocolService_ServiceGetConfiguration_0(ctx context.Context, marshaler runtime.Marshaler, server ProtocolServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq ServiceGetConfiguration_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := server.ServiceGetConfiguration(ctx, &protoReq)\n\treturn msg, metadata, err\n\n}\n\nfunc request_ProtocolService_ContactRequestReference_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq ContactRequestReference_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := client.ContactRequestReference(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))\n\treturn msg, metadata, err\n\n}\n\nfunc local_request_ProtocolService_ContactRequestReference_0(ctx context.Context, marshaler runtime.Marshaler, server ProtocolServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq ContactRequestReference_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := server.ContactRequestReference(ctx, &protoReq)\n\treturn msg, metadata, err\n\n}\n\nfunc request_ProtocolService_ContactRequestDisable_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq ContactRequestDisable_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := client.ContactRequestDisable(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))\n\treturn msg, metadata, err\n\n}\n\nfunc local_request_ProtocolService_ContactRequestDisable_0(ctx context.Context, marshaler runtime.Marshaler, server ProtocolServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq ContactRequestDisable_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := server.ContactRequestDisable(ctx, &protoReq)\n\treturn msg, metadata, err\n\n}\n\nfunc request_ProtocolService_ContactRequestEnable_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq ContactRequestEnable_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := client.ContactRequestEnable(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))\n\treturn msg, metadata, err\n\n}\n\nfunc local_request_ProtocolService_ContactRequestEnable_0(ctx context.Context, marshaler runtime.Marshaler, server ProtocolServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq ContactRequestEnable_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := server.ContactRequestEnable(ctx, &protoReq)\n\treturn msg, metadata, err\n\n}\n\nfunc request_ProtocolService_ContactRequestResetReference_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq ContactRequestResetReference_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := client.ContactRequestResetReference(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))\n\treturn msg, metadata, err\n\n}\n\nfunc local_request_ProtocolService_ContactRequestResetReference_0(ctx context.Context, marshaler runtime.Marshaler, server ProtocolServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq ContactRequestResetReference_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := server.ContactRequestResetReference(ctx, &protoReq)\n\treturn msg, metadata, err\n\n}\n\nfunc request_ProtocolService_ContactRequestSend_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq ContactRequestSend_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := client.ContactRequestSend(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))\n\treturn msg, metadata, err\n\n}\n\nfunc local_request_ProtocolService_ContactRequestSend_0(ctx context.Context, marshaler runtime.Marshaler, server ProtocolServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq ContactRequestSend_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := server.ContactRequestSend(ctx, &protoReq)\n\treturn msg, metadata, err\n\n}\n\nfunc request_ProtocolService_ContactRequestAccept_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq ContactRequestAccept_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := client.ContactRequestAccept(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))\n\treturn msg, metadata, err\n\n}\n\nfunc local_request_ProtocolService_ContactRequestAccept_0(ctx context.Context, marshaler runtime.Marshaler, server ProtocolServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq ContactRequestAccept_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := server.ContactRequestAccept(ctx, &protoReq)\n\treturn msg, metadata, err\n\n}\n\nfunc request_ProtocolService_ContactRequestDiscard_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq ContactRequestDiscard_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := client.ContactRequestDiscard(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))\n\treturn msg, metadata, err\n\n}\n\nfunc local_request_ProtocolService_ContactRequestDiscard_0(ctx context.Context, marshaler runtime.Marshaler, server ProtocolServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq ContactRequestDiscard_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := server.ContactRequestDiscard(ctx, &protoReq)\n\treturn msg, metadata, err\n\n}\n\nfunc request_ProtocolService_ShareContact_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq ShareContact_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := client.ShareContact(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))\n\treturn msg, metadata, err\n\n}\n\nfunc local_request_ProtocolService_ShareContact_0(ctx context.Context, marshaler runtime.Marshaler, server ProtocolServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq ShareContact_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := server.ShareContact(ctx, &protoReq)\n\treturn msg, metadata, err\n\n}\n\nfunc request_ProtocolService_DecodeContact_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq DecodeContact_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := client.DecodeContact(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))\n\treturn msg, metadata, err\n\n}\n\nfunc local_request_ProtocolService_DecodeContact_0(ctx context.Context, marshaler runtime.Marshaler, server ProtocolServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq DecodeContact_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := server.DecodeContact(ctx, &protoReq)\n\treturn msg, metadata, err\n\n}\n\nfunc request_ProtocolService_ContactBlock_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq ContactBlock_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := client.ContactBlock(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))\n\treturn msg, metadata, err\n\n}\n\nfunc local_request_ProtocolService_ContactBlock_0(ctx context.Context, marshaler runtime.Marshaler, server ProtocolServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq ContactBlock_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := server.ContactBlock(ctx, &protoReq)\n\treturn msg, metadata, err\n\n}\n\nfunc request_ProtocolService_ContactUnblock_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq ContactUnblock_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := client.ContactUnblock(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))\n\treturn msg, metadata, err\n\n}\n\nfunc local_request_ProtocolService_ContactUnblock_0(ctx context.Context, marshaler runtime.Marshaler, server ProtocolServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq ContactUnblock_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := server.ContactUnblock(ctx, &protoReq)\n\treturn msg, metadata, err\n\n}\n\nfunc request_ProtocolService_ContactAliasKeySend_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq ContactAliasKeySend_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := client.ContactAliasKeySend(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))\n\treturn msg, metadata, err\n\n}\n\nfunc local_request_ProtocolService_ContactAliasKeySend_0(ctx context.Context, marshaler runtime.Marshaler, server ProtocolServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq ContactAliasKeySend_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := server.ContactAliasKeySend(ctx, &protoReq)\n\treturn msg, metadata, err\n\n}\n\nfunc request_ProtocolService_MultiMemberGroupCreate_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq MultiMemberGroupCreate_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := client.MultiMemberGroupCreate(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))\n\treturn msg, metadata, err\n\n}\n\nfunc local_request_ProtocolService_MultiMemberGroupCreate_0(ctx context.Context, marshaler runtime.Marshaler, server ProtocolServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq MultiMemberGroupCreate_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := server.MultiMemberGroupCreate(ctx, &protoReq)\n\treturn msg, metadata, err\n\n}\n\nfunc request_ProtocolService_MultiMemberGroupJoin_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq MultiMemberGroupJoin_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := client.MultiMemberGroupJoin(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))\n\treturn msg, metadata, err\n\n}\n\nfunc local_request_ProtocolService_MultiMemberGroupJoin_0(ctx context.Context, marshaler runtime.Marshaler, server ProtocolServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq MultiMemberGroupJoin_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := server.MultiMemberGroupJoin(ctx, &protoReq)\n\treturn msg, metadata, err\n\n}\n\nfunc request_ProtocolService_MultiMemberGroupLeave_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq MultiMemberGroupLeave_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := client.MultiMemberGroupLeave(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))\n\treturn msg, metadata, err\n\n}\n\nfunc local_request_ProtocolService_MultiMemberGroupLeave_0(ctx context.Context, marshaler runtime.Marshaler, server ProtocolServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq MultiMemberGroupLeave_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := server.MultiMemberGroupLeave(ctx, &protoReq)\n\treturn msg, metadata, err\n\n}\n\nfunc request_ProtocolService_MultiMemberGroupAliasResolverDisclose_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq MultiMemberGroupAliasResolverDisclose_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := client.MultiMemberGroupAliasResolverDisclose(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))\n\treturn msg, metadata, err\n\n}\n\nfunc local_request_ProtocolService_MultiMemberGroupAliasResolverDisclose_0(ctx context.Context, marshaler runtime.Marshaler, server ProtocolServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq MultiMemberGroupAliasResolverDisclose_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := server.MultiMemberGroupAliasResolverDisclose(ctx, &protoReq)\n\treturn msg, metadata, err\n\n}\n\nfunc request_ProtocolService_MultiMemberGroupAdminRoleGrant_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq MultiMemberGroupAdminRoleGrant_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := client.MultiMemberGroupAdminRoleGrant(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))\n\treturn msg, metadata, err\n\n}\n\nfunc local_request_ProtocolService_MultiMemberGroupAdminRoleGrant_0(ctx context.Context, marshaler runtime.Marshaler, server ProtocolServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq MultiMemberGroupAdminRoleGrant_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := server.MultiMemberGroupAdminRoleGrant(ctx, &protoReq)\n\treturn msg, metadata, err\n\n}\n\nfunc request_ProtocolService_MultiMemberGroupInvitationCreate_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq MultiMemberGroupInvitationCreate_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := client.MultiMemberGroupInvitationCreate(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))\n\treturn msg, metadata, err\n\n}\n\nfunc local_request_ProtocolService_MultiMemberGroupInvitationCreate_0(ctx context.Context, marshaler runtime.Marshaler, server ProtocolServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq MultiMemberGroupInvitationCreate_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := server.MultiMemberGroupInvitationCreate(ctx, &protoReq)\n\treturn msg, metadata, err\n\n}\n\nfunc request_ProtocolService_AppMetadataSend_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq AppMetadataSend_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := client.AppMetadataSend(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))\n\treturn msg, metadata, err\n\n}\n\nfunc local_request_ProtocolService_AppMetadataSend_0(ctx context.Context, marshaler runtime.Marshaler, server ProtocolServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq AppMetadataSend_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := server.AppMetadataSend(ctx, &protoReq)\n\treturn msg, metadata, err\n\n}\n\nfunc request_ProtocolService_AppMessageSend_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq AppMessageSend_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := client.AppMessageSend(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))\n\treturn msg, metadata, err\n\n}\n\nfunc local_request_ProtocolService_AppMessageSend_0(ctx context.Context, marshaler runtime.Marshaler, server ProtocolServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq AppMessageSend_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := server.AppMessageSend(ctx, &protoReq)\n\treturn msg, metadata, err\n\n}\n\nfunc request_ProtocolService_GroupMetadataList_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (ProtocolService_GroupMetadataListClient, runtime.ServerMetadata, error) {\n\tvar protoReq GroupMetadataList_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tstream, err := client.GroupMetadataList(ctx, &protoReq)\n\tif err != nil {\n\t\treturn nil, metadata, err\n\t}\n\theader, err := stream.Header()\n\tif err != nil {\n\t\treturn nil, metadata, err\n\t}\n\tmetadata.HeaderMD = header\n\treturn stream, metadata, nil\n\n}\n\nfunc request_ProtocolService_GroupMessageList_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (ProtocolService_GroupMessageListClient, runtime.ServerMetadata, error) {\n\tvar protoReq GroupMessageList_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tstream, err := client.GroupMessageList(ctx, &protoReq)\n\tif err != nil {\n\t\treturn nil, metadata, err\n\t}\n\theader, err := stream.Header()\n\tif err != nil {\n\t\treturn nil, metadata, err\n\t}\n\tmetadata.HeaderMD = header\n\treturn stream, metadata, nil\n\n}\n\nfunc request_ProtocolService_GroupInfo_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq GroupInfo_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := client.GroupInfo(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))\n\treturn msg, metadata, err\n\n}\n\nfunc local_request_ProtocolService_GroupInfo_0(ctx context.Context, marshaler runtime.Marshaler, server ProtocolServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq GroupInfo_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := server.GroupInfo(ctx, &protoReq)\n\treturn msg, metadata, err\n\n}\n\nfunc request_ProtocolService_ActivateGroup_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq ActivateGroup_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := client.ActivateGroup(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))\n\treturn msg, metadata, err\n\n}\n\nfunc local_request_ProtocolService_ActivateGroup_0(ctx context.Context, marshaler runtime.Marshaler, server ProtocolServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq ActivateGroup_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := server.ActivateGroup(ctx, &protoReq)\n\treturn msg, metadata, err\n\n}\n\nfunc request_ProtocolService_DeactivateGroup_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq DeactivateGroup_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := client.DeactivateGroup(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))\n\treturn msg, metadata, err\n\n}\n\nfunc local_request_ProtocolService_DeactivateGroup_0(ctx context.Context, marshaler runtime.Marshaler, server ProtocolServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq DeactivateGroup_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := server.DeactivateGroup(ctx, &protoReq)\n\treturn msg, metadata, err\n\n}\n\nfunc request_ProtocolService_GroupDeviceStatus_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (ProtocolService_GroupDeviceStatusClient, runtime.ServerMetadata, error) {\n\tvar protoReq GroupDeviceStatus_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tstream, err := client.GroupDeviceStatus(ctx, &protoReq)\n\tif err != nil {\n\t\treturn nil, metadata, err\n\t}\n\theader, err := stream.Header()\n\tif err != nil {\n\t\treturn nil, metadata, err\n\t}\n\tmetadata.HeaderMD = header\n\treturn stream, metadata, nil\n\n}\n\nfunc request_ProtocolService_DebugListGroups_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (ProtocolService_DebugListGroupsClient, runtime.ServerMetadata, error) {\n\tvar protoReq DebugListGroups_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tstream, err := client.DebugListGroups(ctx, &protoReq)\n\tif err != nil {\n\t\treturn nil, metadata, err\n\t}\n\theader, err := stream.Header()\n\tif err != nil {\n\t\treturn nil, metadata, err\n\t}\n\tmetadata.HeaderMD = header\n\treturn stream, metadata, nil\n\n}\n\nfunc request_ProtocolService_DebugInspectGroupStore_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (ProtocolService_DebugInspectGroupStoreClient, runtime.ServerMetadata, error) {\n\tvar protoReq DebugInspectGroupStore_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tstream, err := client.DebugInspectGroupStore(ctx, &protoReq)\n\tif err != nil {\n\t\treturn nil, metadata, err\n\t}\n\theader, err := stream.Header()\n\tif err != nil {\n\t\treturn nil, metadata, err\n\t}\n\tmetadata.HeaderMD = header\n\treturn stream, metadata, nil\n\n}\n\nfunc request_ProtocolService_DebugGroup_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq DebugGroup_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := client.DebugGroup(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))\n\treturn msg, metadata, err\n\n}\n\nfunc local_request_ProtocolService_DebugGroup_0(ctx context.Context, marshaler runtime.Marshaler, server ProtocolServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq DebugGroup_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := server.DebugGroup(ctx, &protoReq)\n\treturn msg, metadata, err\n\n}\n\nfunc request_ProtocolService_SystemInfo_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq SystemInfo_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := client.SystemInfo(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))\n\treturn msg, metadata, err\n\n}\n\nfunc local_request_ProtocolService_SystemInfo_0(ctx context.Context, marshaler runtime.Marshaler, server ProtocolServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq SystemInfo_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := server.SystemInfo(ctx, &protoReq)\n\treturn msg, metadata, err\n\n}\n\nfunc request_ProtocolService_CredentialVerificationServiceInitFlow_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq CredentialVerificationServiceInitFlow_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := client.CredentialVerificationServiceInitFlow(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))\n\treturn msg, metadata, err\n\n}\n\nfunc local_request_ProtocolService_CredentialVerificationServiceInitFlow_0(ctx context.Context, marshaler runtime.Marshaler, server ProtocolServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq CredentialVerificationServiceInitFlow_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := server.CredentialVerificationServiceInitFlow(ctx, &protoReq)\n\treturn msg, metadata, err\n\n}\n\nfunc request_ProtocolService_CredentialVerificationServiceCompleteFlow_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq CredentialVerificationServiceCompleteFlow_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := client.CredentialVerificationServiceCompleteFlow(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))\n\treturn msg, metadata, err\n\n}\n\nfunc local_request_ProtocolService_CredentialVerificationServiceCompleteFlow_0(ctx context.Context, marshaler runtime.Marshaler, server ProtocolServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq CredentialVerificationServiceCompleteFlow_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := server.CredentialVerificationServiceCompleteFlow(ctx, &protoReq)\n\treturn msg, metadata, err\n\n}\n\nfunc request_ProtocolService_VerifiedCredentialsList_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (ProtocolService_VerifiedCredentialsListClient, runtime.ServerMetadata, error) {\n\tvar protoReq VerifiedCredentialsList_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tstream, err := client.VerifiedCredentialsList(ctx, &protoReq)\n\tif err != nil {\n\t\treturn nil, metadata, err\n\t}\n\theader, err := stream.Header()\n\tif err != nil {\n\t\treturn nil, metadata, err\n\t}\n\tmetadata.HeaderMD = header\n\treturn stream, metadata, nil\n\n}\n\nfunc request_ProtocolService_ReplicationServiceRegisterGroup_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq ReplicationServiceRegisterGroup_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := client.ReplicationServiceRegisterGroup(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))\n\treturn msg, metadata, err\n\n}\n\nfunc local_request_ProtocolService_ReplicationServiceRegisterGroup_0(ctx context.Context, marshaler runtime.Marshaler, server ProtocolServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq ReplicationServiceRegisterGroup_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := server.ReplicationServiceRegisterGroup(ctx, &protoReq)\n\treturn msg, metadata, err\n\n}\n\nfunc request_ProtocolService_PeerList_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq PeerList_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := client.PeerList(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))\n\treturn msg, metadata, err\n\n}\n\nfunc local_request_ProtocolService_PeerList_0(ctx context.Context, marshaler runtime.Marshaler, server ProtocolServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq PeerList_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := server.PeerList(ctx, &protoReq)\n\treturn msg, metadata, err\n\n}\n\nfunc request_ProtocolService_OutOfStoreReceive_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq OutOfStoreReceive_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := client.OutOfStoreReceive(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))\n\treturn msg, metadata, err\n\n}\n\nfunc local_request_ProtocolService_OutOfStoreReceive_0(ctx context.Context, marshaler runtime.Marshaler, server ProtocolServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq OutOfStoreReceive_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := server.OutOfStoreReceive(ctx, &protoReq)\n\treturn msg, metadata, err\n\n}\n\nfunc request_ProtocolService_OutOfStoreSeal_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq OutOfStoreSeal_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := client.OutOfStoreSeal(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))\n\treturn msg, metadata, err\n\n}\n\nfunc local_request_ProtocolService_OutOfStoreSeal_0(ctx context.Context, marshaler runtime.Marshaler, server ProtocolServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq OutOfStoreSeal_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := server.OutOfStoreSeal(ctx, &protoReq)\n\treturn msg, metadata, err\n\n}\n\nfunc request_ProtocolService_RefreshContactRequest_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq RefreshContactRequest_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := client.RefreshContactRequest(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))\n\treturn msg, metadata, err\n\n}\n\nfunc local_request_ProtocolService_RefreshContactRequest_0(ctx context.Context, marshaler runtime.Marshaler, server ProtocolServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq RefreshContactRequest_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := server.RefreshContactRequest(ctx, &protoReq)\n\treturn msg, metadata, err\n\n}\n\n// RegisterProtocolServiceHandlerServer registers the http handlers for service ProtocolService to \"mux\".\n// UnaryRPC     :call ProtocolServiceServer directly.\n// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.\n// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterProtocolServiceHandlerFromEndpoint instead.\nfunc RegisterProtocolServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server ProtocolServiceServer) error {\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_ServiceExportData_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\terr := status.Error(codes.Unimplemented, \"streaming calls are not yet supported in the in-process transport\")\n\t\t_, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\treturn\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_ServiceGetConfiguration_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tvar stream runtime.ServerTransportStream\n\t\tctx = grpc.NewContextWithServerTransportStream(ctx, &stream)\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := local_request_ProtocolService_ServiceGetConfiguration_0(rctx, inboundMarshaler, server, req, pathParams)\n\t\tmd.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_ServiceGetConfiguration_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_ContactRequestReference_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tvar stream runtime.ServerTransportStream\n\t\tctx = grpc.NewContextWithServerTransportStream(ctx, &stream)\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := local_request_ProtocolService_ContactRequestReference_0(rctx, inboundMarshaler, server, req, pathParams)\n\t\tmd.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_ContactRequestReference_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_ContactRequestDisable_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tvar stream runtime.ServerTransportStream\n\t\tctx = grpc.NewContextWithServerTransportStream(ctx, &stream)\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := local_request_ProtocolService_ContactRequestDisable_0(rctx, inboundMarshaler, server, req, pathParams)\n\t\tmd.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_ContactRequestDisable_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_ContactRequestEnable_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tvar stream runtime.ServerTransportStream\n\t\tctx = grpc.NewContextWithServerTransportStream(ctx, &stream)\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := local_request_ProtocolService_ContactRequestEnable_0(rctx, inboundMarshaler, server, req, pathParams)\n\t\tmd.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_ContactRequestEnable_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_ContactRequestResetReference_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tvar stream runtime.ServerTransportStream\n\t\tctx = grpc.NewContextWithServerTransportStream(ctx, &stream)\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := local_request_ProtocolService_ContactRequestResetReference_0(rctx, inboundMarshaler, server, req, pathParams)\n\t\tmd.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_ContactRequestResetReference_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_ContactRequestSend_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tvar stream runtime.ServerTransportStream\n\t\tctx = grpc.NewContextWithServerTransportStream(ctx, &stream)\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := local_request_ProtocolService_ContactRequestSend_0(rctx, inboundMarshaler, server, req, pathParams)\n\t\tmd.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_ContactRequestSend_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_ContactRequestAccept_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tvar stream runtime.ServerTransportStream\n\t\tctx = grpc.NewContextWithServerTransportStream(ctx, &stream)\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := local_request_ProtocolService_ContactRequestAccept_0(rctx, inboundMarshaler, server, req, pathParams)\n\t\tmd.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_ContactRequestAccept_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_ContactRequestDiscard_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tvar stream runtime.ServerTransportStream\n\t\tctx = grpc.NewContextWithServerTransportStream(ctx, &stream)\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := local_request_ProtocolService_ContactRequestDiscard_0(rctx, inboundMarshaler, server, req, pathParams)\n\t\tmd.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_ContactRequestDiscard_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_ShareContact_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tvar stream runtime.ServerTransportStream\n\t\tctx = grpc.NewContextWithServerTransportStream(ctx, &stream)\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := local_request_ProtocolService_ShareContact_0(rctx, inboundMarshaler, server, req, pathParams)\n\t\tmd.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_ShareContact_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_DecodeContact_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tvar stream runtime.ServerTransportStream\n\t\tctx = grpc.NewContextWithServerTransportStream(ctx, &stream)\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := local_request_ProtocolService_DecodeContact_0(rctx, inboundMarshaler, server, req, pathParams)\n\t\tmd.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_DecodeContact_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_ContactBlock_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tvar stream runtime.ServerTransportStream\n\t\tctx = grpc.NewContextWithServerTransportStream(ctx, &stream)\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := local_request_ProtocolService_ContactBlock_0(rctx, inboundMarshaler, server, req, pathParams)\n\t\tmd.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_ContactBlock_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_ContactUnblock_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tvar stream runtime.ServerTransportStream\n\t\tctx = grpc.NewContextWithServerTransportStream(ctx, &stream)\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := local_request_ProtocolService_ContactUnblock_0(rctx, inboundMarshaler, server, req, pathParams)\n\t\tmd.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_ContactUnblock_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_ContactAliasKeySend_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tvar stream runtime.ServerTransportStream\n\t\tctx = grpc.NewContextWithServerTransportStream(ctx, &stream)\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := local_request_ProtocolService_ContactAliasKeySend_0(rctx, inboundMarshaler, server, req, pathParams)\n\t\tmd.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_ContactAliasKeySend_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_MultiMemberGroupCreate_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tvar stream runtime.ServerTransportStream\n\t\tctx = grpc.NewContextWithServerTransportStream(ctx, &stream)\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := local_request_ProtocolService_MultiMemberGroupCreate_0(rctx, inboundMarshaler, server, req, pathParams)\n\t\tmd.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_MultiMemberGroupCreate_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_MultiMemberGroupJoin_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tvar stream runtime.ServerTransportStream\n\t\tctx = grpc.NewContextWithServerTransportStream(ctx, &stream)\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := local_request_ProtocolService_MultiMemberGroupJoin_0(rctx, inboundMarshaler, server, req, pathParams)\n\t\tmd.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_MultiMemberGroupJoin_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_MultiMemberGroupLeave_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tvar stream runtime.ServerTransportStream\n\t\tctx = grpc.NewContextWithServerTransportStream(ctx, &stream)\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := local_request_ProtocolService_MultiMemberGroupLeave_0(rctx, inboundMarshaler, server, req, pathParams)\n\t\tmd.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_MultiMemberGroupLeave_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_MultiMemberGroupAliasResolverDisclose_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tvar stream runtime.ServerTransportStream\n\t\tctx = grpc.NewContextWithServerTransportStream(ctx, &stream)\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := local_request_ProtocolService_MultiMemberGroupAliasResolverDisclose_0(rctx, inboundMarshaler, server, req, pathParams)\n\t\tmd.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_MultiMemberGroupAliasResolverDisclose_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_MultiMemberGroupAdminRoleGrant_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tvar stream runtime.ServerTransportStream\n\t\tctx = grpc.NewContextWithServerTransportStream(ctx, &stream)\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := local_request_ProtocolService_MultiMemberGroupAdminRoleGrant_0(rctx, inboundMarshaler, server, req, pathParams)\n\t\tmd.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_MultiMemberGroupAdminRoleGrant_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_MultiMemberGroupInvitationCreate_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tvar stream runtime.ServerTransportStream\n\t\tctx = grpc.NewContextWithServerTransportStream(ctx, &stream)\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := local_request_ProtocolService_MultiMemberGroupInvitationCreate_0(rctx, inboundMarshaler, server, req, pathParams)\n\t\tmd.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_MultiMemberGroupInvitationCreate_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_AppMetadataSend_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tvar stream runtime.ServerTransportStream\n\t\tctx = grpc.NewContextWithServerTransportStream(ctx, &stream)\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := local_request_ProtocolService_AppMetadataSend_0(rctx, inboundMarshaler, server, req, pathParams)\n\t\tmd.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_AppMetadataSend_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_AppMessageSend_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tvar stream runtime.ServerTransportStream\n\t\tctx = grpc.NewContextWithServerTransportStream(ctx, &stream)\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := local_request_ProtocolService_AppMessageSend_0(rctx, inboundMarshaler, server, req, pathParams)\n\t\tmd.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_AppMessageSend_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_GroupMetadataList_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\terr := status.Error(codes.Unimplemented, \"streaming calls are not yet supported in the in-process transport\")\n\t\t_, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\treturn\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_GroupMessageList_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\terr := status.Error(codes.Unimplemented, \"streaming calls are not yet supported in the in-process transport\")\n\t\t_, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\treturn\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_GroupInfo_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tvar stream runtime.ServerTransportStream\n\t\tctx = grpc.NewContextWithServerTransportStream(ctx, &stream)\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := local_request_ProtocolService_GroupInfo_0(rctx, inboundMarshaler, server, req, pathParams)\n\t\tmd.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_GroupInfo_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_ActivateGroup_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tvar stream runtime.ServerTransportStream\n\t\tctx = grpc.NewContextWithServerTransportStream(ctx, &stream)\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := local_request_ProtocolService_ActivateGroup_0(rctx, inboundMarshaler, server, req, pathParams)\n\t\tmd.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_ActivateGroup_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_DeactivateGroup_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tvar stream runtime.ServerTransportStream\n\t\tctx = grpc.NewContextWithServerTransportStream(ctx, &stream)\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := local_request_ProtocolService_DeactivateGroup_0(rctx, inboundMarshaler, server, req, pathParams)\n\t\tmd.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_DeactivateGroup_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_GroupDeviceStatus_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\terr := status.Error(codes.Unimplemented, \"streaming calls are not yet supported in the in-process transport\")\n\t\t_, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\treturn\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_DebugListGroups_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\terr := status.Error(codes.Unimplemented, \"streaming calls are not yet supported in the in-process transport\")\n\t\t_, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\treturn\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_DebugInspectGroupStore_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\terr := status.Error(codes.Unimplemented, \"streaming calls are not yet supported in the in-process transport\")\n\t\t_, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\treturn\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_DebugGroup_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tvar stream runtime.ServerTransportStream\n\t\tctx = grpc.NewContextWithServerTransportStream(ctx, &stream)\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := local_request_ProtocolService_DebugGroup_0(rctx, inboundMarshaler, server, req, pathParams)\n\t\tmd.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_DebugGroup_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_SystemInfo_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tvar stream runtime.ServerTransportStream\n\t\tctx = grpc.NewContextWithServerTransportStream(ctx, &stream)\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := local_request_ProtocolService_SystemInfo_0(rctx, inboundMarshaler, server, req, pathParams)\n\t\tmd.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_SystemInfo_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_CredentialVerificationServiceInitFlow_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tvar stream runtime.ServerTransportStream\n\t\tctx = grpc.NewContextWithServerTransportStream(ctx, &stream)\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := local_request_ProtocolService_CredentialVerificationServiceInitFlow_0(rctx, inboundMarshaler, server, req, pathParams)\n\t\tmd.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_CredentialVerificationServiceInitFlow_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_CredentialVerificationServiceCompleteFlow_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tvar stream runtime.ServerTransportStream\n\t\tctx = grpc.NewContextWithServerTransportStream(ctx, &stream)\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := local_request_ProtocolService_CredentialVerificationServiceCompleteFlow_0(rctx, inboundMarshaler, server, req, pathParams)\n\t\tmd.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_CredentialVerificationServiceCompleteFlow_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_VerifiedCredentialsList_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\terr := status.Error(codes.Unimplemented, \"streaming calls are not yet supported in the in-process transport\")\n\t\t_, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\treturn\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_ReplicationServiceRegisterGroup_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tvar stream runtime.ServerTransportStream\n\t\tctx = grpc.NewContextWithServerTransportStream(ctx, &stream)\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := local_request_ProtocolService_ReplicationServiceRegisterGroup_0(rctx, inboundMarshaler, server, req, pathParams)\n\t\tmd.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_ReplicationServiceRegisterGroup_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_PeerList_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tvar stream runtime.ServerTransportStream\n\t\tctx = grpc.NewContextWithServerTransportStream(ctx, &stream)\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := local_request_ProtocolService_PeerList_0(rctx, inboundMarshaler, server, req, pathParams)\n\t\tmd.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_PeerList_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_OutOfStoreReceive_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tvar stream runtime.ServerTransportStream\n\t\tctx = grpc.NewContextWithServerTransportStream(ctx, &stream)\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := local_request_ProtocolService_OutOfStoreReceive_0(rctx, inboundMarshaler, server, req, pathParams)\n\t\tmd.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_OutOfStoreReceive_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_OutOfStoreSeal_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tvar stream runtime.ServerTransportStream\n\t\tctx = grpc.NewContextWithServerTransportStream(ctx, &stream)\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := local_request_ProtocolService_OutOfStoreSeal_0(rctx, inboundMarshaler, server, req, pathParams)\n\t\tmd.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_OutOfStoreSeal_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_RefreshContactRequest_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tvar stream runtime.ServerTransportStream\n\t\tctx = grpc.NewContextWithServerTransportStream(ctx, &stream)\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := local_request_ProtocolService_RefreshContactRequest_0(rctx, inboundMarshaler, server, req, pathParams)\n\t\tmd.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_RefreshContactRequest_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\treturn nil\n}\n\n// RegisterProtocolServiceHandlerFromEndpoint is same as RegisterProtocolServiceHandler but\n// automatically dials to \"endpoint\" and closes the connection when \"ctx\" gets done.\nfunc RegisterProtocolServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {\n\tconn, err := grpc.Dial(endpoint, opts...)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer func() {\n\t\tif err != nil {\n\t\t\tif cerr := conn.Close(); cerr != nil {\n\t\t\t\tgrpclog.Infof(\"Failed to close conn to %s: %v\", endpoint, cerr)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\tgo func() {\n\t\t\t<-ctx.Done()\n\t\t\tif cerr := conn.Close(); cerr != nil {\n\t\t\t\tgrpclog.Infof(\"Failed to close conn to %s: %v\", endpoint, cerr)\n\t\t\t}\n\t\t}()\n\t}()\n\n\treturn RegisterProtocolServiceHandler(ctx, mux, conn)\n}\n\n// RegisterProtocolServiceHandler registers the http handlers for service ProtocolService to \"mux\".\n// The handlers forward requests to the grpc endpoint over \"conn\".\nfunc RegisterProtocolServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {\n\treturn RegisterProtocolServiceHandlerClient(ctx, mux, NewProtocolServiceClient(conn))\n}\n\n// RegisterProtocolServiceHandlerClient registers the http handlers for service ProtocolService\n// to \"mux\". The handlers forward requests to the grpc endpoint over the given implementation of \"ProtocolServiceClient\".\n// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in \"ProtocolServiceClient\"\n// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in\n// \"ProtocolServiceClient\" to call the correct interceptors.\nfunc RegisterProtocolServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client ProtocolServiceClient) error {\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_ServiceExportData_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := request_ProtocolService_ServiceExportData_0(rctx, inboundMarshaler, client, req, pathParams)\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_ServiceExportData_0(ctx, mux, outboundMarshaler, w, req, func() (proto.Message, error) { return resp.Recv() }, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_ServiceGetConfiguration_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := request_ProtocolService_ServiceGetConfiguration_0(rctx, inboundMarshaler, client, req, pathParams)\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_ServiceGetConfiguration_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_ContactRequestReference_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := request_ProtocolService_ContactRequestReference_0(rctx, inboundMarshaler, client, req, pathParams)\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_ContactRequestReference_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_ContactRequestDisable_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := request_ProtocolService_ContactRequestDisable_0(rctx, inboundMarshaler, client, req, pathParams)\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_ContactRequestDisable_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_ContactRequestEnable_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := request_ProtocolService_ContactRequestEnable_0(rctx, inboundMarshaler, client, req, pathParams)\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_ContactRequestEnable_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_ContactRequestResetReference_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := request_ProtocolService_ContactRequestResetReference_0(rctx, inboundMarshaler, client, req, pathParams)\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_ContactRequestResetReference_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_ContactRequestSend_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := request_ProtocolService_ContactRequestSend_0(rctx, inboundMarshaler, client, req, pathParams)\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_ContactRequestSend_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_ContactRequestAccept_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := request_ProtocolService_ContactRequestAccept_0(rctx, inboundMarshaler, client, req, pathParams)\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_ContactRequestAccept_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_ContactRequestDiscard_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := request_ProtocolService_ContactRequestDiscard_0(rctx, inboundMarshaler, client, req, pathParams)\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_ContactRequestDiscard_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_ShareContact_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := request_ProtocolService_ShareContact_0(rctx, inboundMarshaler, client, req, pathParams)\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_ShareContact_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_DecodeContact_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := request_ProtocolService_DecodeContact_0(rctx, inboundMarshaler, client, req, pathParams)\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_DecodeContact_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_ContactBlock_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := request_ProtocolService_ContactBlock_0(rctx, inboundMarshaler, client, req, pathParams)\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_ContactBlock_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_ContactUnblock_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := request_ProtocolService_ContactUnblock_0(rctx, inboundMarshaler, client, req, pathParams)\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_ContactUnblock_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_ContactAliasKeySend_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := request_ProtocolService_ContactAliasKeySend_0(rctx, inboundMarshaler, client, req, pathParams)\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_ContactAliasKeySend_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_MultiMemberGroupCreate_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := request_ProtocolService_MultiMemberGroupCreate_0(rctx, inboundMarshaler, client, req, pathParams)\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_MultiMemberGroupCreate_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_MultiMemberGroupJoin_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := request_ProtocolService_MultiMemberGroupJoin_0(rctx, inboundMarshaler, client, req, pathParams)\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_MultiMemberGroupJoin_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_MultiMemberGroupLeave_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := request_ProtocolService_MultiMemberGroupLeave_0(rctx, inboundMarshaler, client, req, pathParams)\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_MultiMemberGroupLeave_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_MultiMemberGroupAliasResolverDisclose_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := request_ProtocolService_MultiMemberGroupAliasResolverDisclose_0(rctx, inboundMarshaler, client, req, pathParams)\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_MultiMemberGroupAliasResolverDisclose_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_MultiMemberGroupAdminRoleGrant_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := request_ProtocolService_MultiMemberGroupAdminRoleGrant_0(rctx, inboundMarshaler, client, req, pathParams)\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_MultiMemberGroupAdminRoleGrant_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_MultiMemberGroupInvitationCreate_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := request_ProtocolService_MultiMemberGroupInvitationCreate_0(rctx, inboundMarshaler, client, req, pathParams)\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_MultiMemberGroupInvitationCreate_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_AppMetadataSend_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := request_ProtocolService_AppMetadataSend_0(rctx, inboundMarshaler, client, req, pathParams)\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_AppMetadataSend_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_AppMessageSend_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := request_ProtocolService_AppMessageSend_0(rctx, inboundMarshaler, client, req, pathParams)\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_AppMessageSend_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_GroupMetadataList_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := request_ProtocolService_GroupMetadataList_0(rctx, inboundMarshaler, client, req, pathParams)\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_GroupMetadataList_0(ctx, mux, outboundMarshaler, w, req, func() (proto.Message, error) { return resp.Recv() }, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_GroupMessageList_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := request_ProtocolService_GroupMessageList_0(rctx, inboundMarshaler, client, req, pathParams)\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_GroupMessageList_0(ctx, mux, outboundMarshaler, w, req, func() (proto.Message, error) { return resp.Recv() }, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_GroupInfo_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := request_ProtocolService_GroupInfo_0(rctx, inboundMarshaler, client, req, pathParams)\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_GroupInfo_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_ActivateGroup_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := request_ProtocolService_ActivateGroup_0(rctx, inboundMarshaler, client, req, pathParams)\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_ActivateGroup_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_DeactivateGroup_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := request_ProtocolService_DeactivateGroup_0(rctx, inboundMarshaler, client, req, pathParams)\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_DeactivateGroup_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_GroupDeviceStatus_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := request_ProtocolService_GroupDeviceStatus_0(rctx, inboundMarshaler, client, req, pathParams)\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_GroupDeviceStatus_0(ctx, mux, outboundMarshaler, w, req, func() (proto.Message, error) { return resp.Recv() }, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_DebugListGroups_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := request_ProtocolService_DebugListGroups_0(rctx, inboundMarshaler, client, req, pathParams)\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_DebugListGroups_0(ctx, mux, outboundMarshaler, w, req, func() (proto.Message, error) { return resp.Recv() }, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_DebugInspectGroupStore_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := request_ProtocolService_DebugInspectGroupStore_0(rctx, inboundMarshaler, client, req, pathParams)\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_DebugInspectGroupStore_0(ctx, mux, outboundMarshaler, w, req, func() (proto.Message, error) { return resp.Recv() }, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_DebugGroup_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := request_ProtocolService_DebugGroup_0(rctx, inboundMarshaler, client, req, pathParams)\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_DebugGroup_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_SystemInfo_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := request_ProtocolService_SystemInfo_0(rctx, inboundMarshaler, client, req, pathParams)\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_SystemInfo_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_CredentialVerificationServiceInitFlow_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := request_ProtocolService_CredentialVerificationServiceInitFlow_0(rctx, inboundMarshaler, client, req, pathParams)\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_CredentialVerificationServiceInitFlow_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_CredentialVerificationServiceCompleteFlow_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := request_ProtocolService_CredentialVerificationServiceCompleteFlow_0(rctx, inboundMarshaler, client, req, pathParams)\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_CredentialVerificationServiceCompleteFlow_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_VerifiedCredentialsList_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := request_ProtocolService_VerifiedCredentialsList_0(rctx, inboundMarshaler, client, req, pathParams)\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_VerifiedCredentialsList_0(ctx, mux, outboundMarshaler, w, req, func() (proto.Message, error) { return resp.Recv() }, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_ReplicationServiceRegisterGroup_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := request_ProtocolService_ReplicationServiceRegisterGroup_0(rctx, inboundMarshaler, client, req, pathParams)\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_ReplicationServiceRegisterGroup_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_PeerList_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := request_ProtocolService_PeerList_0(rctx, inboundMarshaler, client, req, pathParams)\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_PeerList_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_OutOfStoreReceive_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := request_ProtocolService_OutOfStoreReceive_0(rctx, inboundMarshaler, client, req, pathParams)\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_OutOfStoreReceive_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_OutOfStoreSeal_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := request_ProtocolService_OutOfStoreSeal_0(rctx, inboundMarshaler, client, req, pathParams)\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_OutOfStoreSeal_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ProtocolService_RefreshContactRequest_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := request_ProtocolService_RefreshContactRequest_0(rctx, inboundMarshaler, client, req, pathParams)\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ProtocolService_RefreshContactRequest_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\treturn nil\n}\n\nvar (\n\tpattern_ProtocolService_ServiceExportData_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{\"weshnet.protocol.v1.ProtocolService\", \"ServiceExportData\"}, \"\", runtime.AssumeColonVerbOpt(true)))\n\n\tpattern_ProtocolService_ServiceGetConfiguration_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{\"weshnet.protocol.v1.ProtocolService\", \"ServiceGetConfiguration\"}, \"\", runtime.AssumeColonVerbOpt(true)))\n\n\tpattern_ProtocolService_ContactRequestReference_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{\"weshnet.protocol.v1.ProtocolService\", \"ContactRequestReference\"}, \"\", runtime.AssumeColonVerbOpt(true)))\n\n\tpattern_ProtocolService_ContactRequestDisable_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{\"weshnet.protocol.v1.ProtocolService\", \"ContactRequestDisable\"}, \"\", runtime.AssumeColonVerbOpt(true)))\n\n\tpattern_ProtocolService_ContactRequestEnable_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{\"weshnet.protocol.v1.ProtocolService\", \"ContactRequestEnable\"}, \"\", runtime.AssumeColonVerbOpt(true)))\n\n\tpattern_ProtocolService_ContactRequestResetReference_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{\"weshnet.protocol.v1.ProtocolService\", \"ContactRequestResetReference\"}, \"\", runtime.AssumeColonVerbOpt(true)))\n\n\tpattern_ProtocolService_ContactRequestSend_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{\"weshnet.protocol.v1.ProtocolService\", \"ContactRequestSend\"}, \"\", runtime.AssumeColonVerbOpt(true)))\n\n\tpattern_ProtocolService_ContactRequestAccept_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{\"weshnet.protocol.v1.ProtocolService\", \"ContactRequestAccept\"}, \"\", runtime.AssumeColonVerbOpt(true)))\n\n\tpattern_ProtocolService_ContactRequestDiscard_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{\"weshnet.protocol.v1.ProtocolService\", \"ContactRequestDiscard\"}, \"\", runtime.AssumeColonVerbOpt(true)))\n\n\tpattern_ProtocolService_ShareContact_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{\"weshnet.protocol.v1.ProtocolService\", \"ShareContact\"}, \"\", runtime.AssumeColonVerbOpt(true)))\n\n\tpattern_ProtocolService_DecodeContact_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{\"weshnet.protocol.v1.ProtocolService\", \"DecodeContact\"}, \"\", runtime.AssumeColonVerbOpt(true)))\n\n\tpattern_ProtocolService_ContactBlock_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{\"weshnet.protocol.v1.ProtocolService\", \"ContactBlock\"}, \"\", runtime.AssumeColonVerbOpt(true)))\n\n\tpattern_ProtocolService_ContactUnblock_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{\"weshnet.protocol.v1.ProtocolService\", \"ContactUnblock\"}, \"\", runtime.AssumeColonVerbOpt(true)))\n\n\tpattern_ProtocolService_ContactAliasKeySend_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{\"weshnet.protocol.v1.ProtocolService\", \"ContactAliasKeySend\"}, \"\", runtime.AssumeColonVerbOpt(true)))\n\n\tpattern_ProtocolService_MultiMemberGroupCreate_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{\"weshnet.protocol.v1.ProtocolService\", \"MultiMemberGroupCreate\"}, \"\", runtime.AssumeColonVerbOpt(true)))\n\n\tpattern_ProtocolService_MultiMemberGroupJoin_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{\"weshnet.protocol.v1.ProtocolService\", \"MultiMemberGroupJoin\"}, \"\", runtime.AssumeColonVerbOpt(true)))\n\n\tpattern_ProtocolService_MultiMemberGroupLeave_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{\"weshnet.protocol.v1.ProtocolService\", \"MultiMemberGroupLeave\"}, \"\", runtime.AssumeColonVerbOpt(true)))\n\n\tpattern_ProtocolService_MultiMemberGroupAliasResolverDisclose_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{\"weshnet.protocol.v1.ProtocolService\", \"MultiMemberGroupAliasResolverDisclose\"}, \"\", runtime.AssumeColonVerbOpt(true)))\n\n\tpattern_ProtocolService_MultiMemberGroupAdminRoleGrant_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{\"weshnet.protocol.v1.ProtocolService\", \"MultiMemberGroupAdminRoleGrant\"}, \"\", runtime.AssumeColonVerbOpt(true)))\n\n\tpattern_ProtocolService_MultiMemberGroupInvitationCreate_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{\"weshnet.protocol.v1.ProtocolService\", \"MultiMemberGroupInvitationCreate\"}, \"\", runtime.AssumeColonVerbOpt(true)))\n\n\tpattern_ProtocolService_AppMetadataSend_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{\"weshnet.protocol.v1.ProtocolService\", \"AppMetadataSend\"}, \"\", runtime.AssumeColonVerbOpt(true)))\n\n\tpattern_ProtocolService_AppMessageSend_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{\"weshnet.protocol.v1.ProtocolService\", \"AppMessageSend\"}, \"\", runtime.AssumeColonVerbOpt(true)))\n\n\tpattern_ProtocolService_GroupMetadataList_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{\"weshnet.protocol.v1.ProtocolService\", \"GroupMetadataList\"}, \"\", runtime.AssumeColonVerbOpt(true)))\n\n\tpattern_ProtocolService_GroupMessageList_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{\"weshnet.protocol.v1.ProtocolService\", \"GroupMessageList\"}, \"\", runtime.AssumeColonVerbOpt(true)))\n\n\tpattern_ProtocolService_GroupInfo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{\"weshnet.protocol.v1.ProtocolService\", \"GroupInfo\"}, \"\", runtime.AssumeColonVerbOpt(true)))\n\n\tpattern_ProtocolService_ActivateGroup_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{\"weshnet.protocol.v1.ProtocolService\", \"ActivateGroup\"}, \"\", runtime.AssumeColonVerbOpt(true)))\n\n\tpattern_ProtocolService_DeactivateGroup_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{\"weshnet.protocol.v1.ProtocolService\", \"DeactivateGroup\"}, \"\", runtime.AssumeColonVerbOpt(true)))\n\n\tpattern_ProtocolService_GroupDeviceStatus_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{\"weshnet.protocol.v1.ProtocolService\", \"GroupDeviceStatus\"}, \"\", runtime.AssumeColonVerbOpt(true)))\n\n\tpattern_ProtocolService_DebugListGroups_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{\"weshnet.protocol.v1.ProtocolService\", \"DebugListGroups\"}, \"\", runtime.AssumeColonVerbOpt(true)))\n\n\tpattern_ProtocolService_DebugInspectGroupStore_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{\"weshnet.protocol.v1.ProtocolService\", \"DebugInspectGroupStore\"}, \"\", runtime.AssumeColonVerbOpt(true)))\n\n\tpattern_ProtocolService_DebugGroup_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{\"weshnet.protocol.v1.ProtocolService\", \"DebugGroup\"}, \"\", runtime.AssumeColonVerbOpt(true)))\n\n\tpattern_ProtocolService_SystemInfo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{\"weshnet.protocol.v1.ProtocolService\", \"SystemInfo\"}, \"\", runtime.AssumeColonVerbOpt(true)))\n\n\tpattern_ProtocolService_CredentialVerificationServiceInitFlow_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{\"weshnet.protocol.v1.ProtocolService\", \"CredentialVerificationServiceInitFlow\"}, \"\", runtime.AssumeColonVerbOpt(true)))\n\n\tpattern_ProtocolService_CredentialVerificationServiceCompleteFlow_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{\"weshnet.protocol.v1.ProtocolService\", \"CredentialVerificationServiceCompleteFlow\"}, \"\", runtime.AssumeColonVerbOpt(true)))\n\n\tpattern_ProtocolService_VerifiedCredentialsList_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{\"weshnet.protocol.v1.ProtocolService\", \"VerifiedCredentialsList\"}, \"\", runtime.AssumeColonVerbOpt(true)))\n\n\tpattern_ProtocolService_ReplicationServiceRegisterGroup_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{\"weshnet.protocol.v1.ProtocolService\", \"ReplicationServiceRegisterGroup\"}, \"\", runtime.AssumeColonVerbOpt(true)))\n\n\tpattern_ProtocolService_PeerList_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{\"weshnet.protocol.v1.ProtocolService\", \"PeerList\"}, \"\", runtime.AssumeColonVerbOpt(true)))\n\n\tpattern_ProtocolService_OutOfStoreReceive_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{\"weshnet.protocol.v1.ProtocolService\", \"OutOfStoreReceive\"}, \"\", runtime.AssumeColonVerbOpt(true)))\n\n\tpattern_ProtocolService_OutOfStoreSeal_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{\"weshnet.protocol.v1.ProtocolService\", \"OutOfStoreSeal\"}, \"\", runtime.AssumeColonVerbOpt(true)))\n\n\tpattern_ProtocolService_RefreshContactRequest_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{\"weshnet.protocol.v1.ProtocolService\", \"RefreshContactRequest\"}, \"\", runtime.AssumeColonVerbOpt(true)))\n)\n\nvar (\n\tforward_ProtocolService_ServiceExportData_0 = runtime.ForwardResponseStream\n\n\tforward_ProtocolService_ServiceGetConfiguration_0 = runtime.ForwardResponseMessage\n\n\tforward_ProtocolService_ContactRequestReference_0 = runtime.ForwardResponseMessage\n\n\tforward_ProtocolService_ContactRequestDisable_0 = runtime.ForwardResponseMessage\n\n\tforward_ProtocolService_ContactRequestEnable_0 = runtime.ForwardResponseMessage\n\n\tforward_ProtocolService_ContactRequestResetReference_0 = runtime.ForwardResponseMessage\n\n\tforward_ProtocolService_ContactRequestSend_0 = runtime.ForwardResponseMessage\n\n\tforward_ProtocolService_ContactRequestAccept_0 = runtime.ForwardResponseMessage\n\n\tforward_ProtocolService_ContactRequestDiscard_0 = runtime.ForwardResponseMessage\n\n\tforward_ProtocolService_ShareContact_0 = runtime.ForwardResponseMessage\n\n\tforward_ProtocolService_DecodeContact_0 = runtime.ForwardResponseMessage\n\n\tforward_ProtocolService_ContactBlock_0 = runtime.ForwardResponseMessage\n\n\tforward_ProtocolService_ContactUnblock_0 = runtime.ForwardResponseMessage\n\n\tforward_ProtocolService_ContactAliasKeySend_0 = runtime.ForwardResponseMessage\n\n\tforward_ProtocolService_MultiMemberGroupCreate_0 = runtime.ForwardResponseMessage\n\n\tforward_ProtocolService_MultiMemberGroupJoin_0 = runtime.ForwardResponseMessage\n\n\tforward_ProtocolService_MultiMemberGroupLeave_0 = runtime.ForwardResponseMessage\n\n\tforward_ProtocolService_MultiMemberGroupAliasResolverDisclose_0 = runtime.ForwardResponseMessage\n\n\tforward_ProtocolService_MultiMemberGroupAdminRoleGrant_0 = runtime.ForwardResponseMessage\n\n\tforward_ProtocolService_MultiMemberGroupInvitationCreate_0 = runtime.ForwardResponseMessage\n\n\tforward_ProtocolService_AppMetadataSend_0 = runtime.ForwardResponseMessage\n\n\tforward_ProtocolService_AppMessageSend_0 = runtime.ForwardResponseMessage\n\n\tforward_ProtocolService_GroupMetadataList_0 = runtime.ForwardResponseStream\n\n\tforward_ProtocolService_GroupMessageList_0 = runtime.ForwardResponseStream\n\n\tforward_ProtocolService_GroupInfo_0 = runtime.ForwardResponseMessage\n\n\tforward_ProtocolService_ActivateGroup_0 = runtime.ForwardResponseMessage\n\n\tforward_ProtocolService_DeactivateGroup_0 = runtime.ForwardResponseMessage\n\n\tforward_ProtocolService_GroupDeviceStatus_0 = runtime.ForwardResponseStream\n\n\tforward_ProtocolService_DebugListGroups_0 = runtime.ForwardResponseStream\n\n\tforward_ProtocolService_DebugInspectGroupStore_0 = runtime.ForwardResponseStream\n\n\tforward_ProtocolService_DebugGroup_0 = runtime.ForwardResponseMessage\n\n\tforward_ProtocolService_SystemInfo_0 = runtime.ForwardResponseMessage\n\n\tforward_ProtocolService_CredentialVerificationServiceInitFlow_0 = runtime.ForwardResponseMessage\n\n\tforward_ProtocolService_CredentialVerificationServiceCompleteFlow_0 = runtime.ForwardResponseMessage\n\n\tforward_ProtocolService_VerifiedCredentialsList_0 = runtime.ForwardResponseStream\n\n\tforward_ProtocolService_ReplicationServiceRegisterGroup_0 = runtime.ForwardResponseMessage\n\n\tforward_ProtocolService_PeerList_0 = runtime.ForwardResponseMessage\n\n\tforward_ProtocolService_OutOfStoreReceive_0 = runtime.ForwardResponseMessage\n\n\tforward_ProtocolService_OutOfStoreSeal_0 = runtime.ForwardResponseMessage\n\n\tforward_ProtocolService_RefreshContactRequest_0 = runtime.ForwardResponseMessage\n)\n"
  },
  {
    "path": "pkg/protocoltypes/protocoltypes_grpc.pb.go",
    "content": "// Code generated by protoc-gen-go-grpc. DO NOT EDIT.\n// versions:\n// - protoc-gen-go-grpc v1.5.1\n// - protoc             (unknown)\n// source: protocoltypes.proto\n\npackage protocoltypes\n\nimport (\n\tcontext \"context\"\n\tgrpc \"google.golang.org/grpc\"\n\tcodes \"google.golang.org/grpc/codes\"\n\tstatus \"google.golang.org/grpc/status\"\n)\n\n// This is a compile-time assertion to ensure that this generated file\n// is compatible with the grpc package it is being compiled against.\n// Requires gRPC-Go v1.64.0 or later.\nconst _ = grpc.SupportPackageIsVersion9\n\nconst (\n\tProtocolService_ServiceExportData_FullMethodName                         = \"/weshnet.protocol.v1.ProtocolService/ServiceExportData\"\n\tProtocolService_ServiceGetConfiguration_FullMethodName                   = \"/weshnet.protocol.v1.ProtocolService/ServiceGetConfiguration\"\n\tProtocolService_ContactRequestReference_FullMethodName                   = \"/weshnet.protocol.v1.ProtocolService/ContactRequestReference\"\n\tProtocolService_ContactRequestDisable_FullMethodName                     = \"/weshnet.protocol.v1.ProtocolService/ContactRequestDisable\"\n\tProtocolService_ContactRequestEnable_FullMethodName                      = \"/weshnet.protocol.v1.ProtocolService/ContactRequestEnable\"\n\tProtocolService_ContactRequestResetReference_FullMethodName              = \"/weshnet.protocol.v1.ProtocolService/ContactRequestResetReference\"\n\tProtocolService_ContactRequestSend_FullMethodName                        = \"/weshnet.protocol.v1.ProtocolService/ContactRequestSend\"\n\tProtocolService_ContactRequestAccept_FullMethodName                      = \"/weshnet.protocol.v1.ProtocolService/ContactRequestAccept\"\n\tProtocolService_ContactRequestDiscard_FullMethodName                     = \"/weshnet.protocol.v1.ProtocolService/ContactRequestDiscard\"\n\tProtocolService_ShareContact_FullMethodName                              = \"/weshnet.protocol.v1.ProtocolService/ShareContact\"\n\tProtocolService_DecodeContact_FullMethodName                             = \"/weshnet.protocol.v1.ProtocolService/DecodeContact\"\n\tProtocolService_ContactBlock_FullMethodName                              = \"/weshnet.protocol.v1.ProtocolService/ContactBlock\"\n\tProtocolService_ContactUnblock_FullMethodName                            = \"/weshnet.protocol.v1.ProtocolService/ContactUnblock\"\n\tProtocolService_ContactAliasKeySend_FullMethodName                       = \"/weshnet.protocol.v1.ProtocolService/ContactAliasKeySend\"\n\tProtocolService_MultiMemberGroupCreate_FullMethodName                    = \"/weshnet.protocol.v1.ProtocolService/MultiMemberGroupCreate\"\n\tProtocolService_MultiMemberGroupJoin_FullMethodName                      = \"/weshnet.protocol.v1.ProtocolService/MultiMemberGroupJoin\"\n\tProtocolService_MultiMemberGroupLeave_FullMethodName                     = \"/weshnet.protocol.v1.ProtocolService/MultiMemberGroupLeave\"\n\tProtocolService_MultiMemberGroupAliasResolverDisclose_FullMethodName     = \"/weshnet.protocol.v1.ProtocolService/MultiMemberGroupAliasResolverDisclose\"\n\tProtocolService_MultiMemberGroupAdminRoleGrant_FullMethodName            = \"/weshnet.protocol.v1.ProtocolService/MultiMemberGroupAdminRoleGrant\"\n\tProtocolService_MultiMemberGroupInvitationCreate_FullMethodName          = \"/weshnet.protocol.v1.ProtocolService/MultiMemberGroupInvitationCreate\"\n\tProtocolService_AppMetadataSend_FullMethodName                           = \"/weshnet.protocol.v1.ProtocolService/AppMetadataSend\"\n\tProtocolService_AppMessageSend_FullMethodName                            = \"/weshnet.protocol.v1.ProtocolService/AppMessageSend\"\n\tProtocolService_GroupMetadataList_FullMethodName                         = \"/weshnet.protocol.v1.ProtocolService/GroupMetadataList\"\n\tProtocolService_GroupMessageList_FullMethodName                          = \"/weshnet.protocol.v1.ProtocolService/GroupMessageList\"\n\tProtocolService_GroupInfo_FullMethodName                                 = \"/weshnet.protocol.v1.ProtocolService/GroupInfo\"\n\tProtocolService_ActivateGroup_FullMethodName                             = \"/weshnet.protocol.v1.ProtocolService/ActivateGroup\"\n\tProtocolService_DeactivateGroup_FullMethodName                           = \"/weshnet.protocol.v1.ProtocolService/DeactivateGroup\"\n\tProtocolService_GroupDeviceStatus_FullMethodName                         = \"/weshnet.protocol.v1.ProtocolService/GroupDeviceStatus\"\n\tProtocolService_DebugListGroups_FullMethodName                           = \"/weshnet.protocol.v1.ProtocolService/DebugListGroups\"\n\tProtocolService_DebugInspectGroupStore_FullMethodName                    = \"/weshnet.protocol.v1.ProtocolService/DebugInspectGroupStore\"\n\tProtocolService_DebugGroup_FullMethodName                                = \"/weshnet.protocol.v1.ProtocolService/DebugGroup\"\n\tProtocolService_SystemInfo_FullMethodName                                = \"/weshnet.protocol.v1.ProtocolService/SystemInfo\"\n\tProtocolService_CredentialVerificationServiceInitFlow_FullMethodName     = \"/weshnet.protocol.v1.ProtocolService/CredentialVerificationServiceInitFlow\"\n\tProtocolService_CredentialVerificationServiceCompleteFlow_FullMethodName = \"/weshnet.protocol.v1.ProtocolService/CredentialVerificationServiceCompleteFlow\"\n\tProtocolService_VerifiedCredentialsList_FullMethodName                   = \"/weshnet.protocol.v1.ProtocolService/VerifiedCredentialsList\"\n\tProtocolService_ReplicationServiceRegisterGroup_FullMethodName           = \"/weshnet.protocol.v1.ProtocolService/ReplicationServiceRegisterGroup\"\n\tProtocolService_PeerList_FullMethodName                                  = \"/weshnet.protocol.v1.ProtocolService/PeerList\"\n\tProtocolService_OutOfStoreReceive_FullMethodName                         = \"/weshnet.protocol.v1.ProtocolService/OutOfStoreReceive\"\n\tProtocolService_OutOfStoreSeal_FullMethodName                            = \"/weshnet.protocol.v1.ProtocolService/OutOfStoreSeal\"\n\tProtocolService_RefreshContactRequest_FullMethodName                     = \"/weshnet.protocol.v1.ProtocolService/RefreshContactRequest\"\n)\n\n// ProtocolServiceClient is the client API for ProtocolService service.\n//\n// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.\n//\n// ProtocolService is the top-level API to manage the Wesh protocol service.\n// Each active Wesh protocol service is considered as a Wesh device and is associated with a Wesh user.\ntype ProtocolServiceClient interface {\n\t// ServiceExportData exports the current data of the protocol service\n\tServiceExportData(ctx context.Context, in *ServiceExportData_Request, opts ...grpc.CallOption) (grpc.ServerStreamingClient[ServiceExportData_Reply], error)\n\t// ServiceGetConfiguration gets the current configuration of the protocol service\n\tServiceGetConfiguration(ctx context.Context, in *ServiceGetConfiguration_Request, opts ...grpc.CallOption) (*ServiceGetConfiguration_Reply, error)\n\t// ContactRequestReference retrieves the information required to create a reference (ie. included in a shareable link) to the current account\n\tContactRequestReference(ctx context.Context, in *ContactRequestReference_Request, opts ...grpc.CallOption) (*ContactRequestReference_Reply, error)\n\t// ContactRequestDisable disables incoming contact requests\n\tContactRequestDisable(ctx context.Context, in *ContactRequestDisable_Request, opts ...grpc.CallOption) (*ContactRequestDisable_Reply, error)\n\t// ContactRequestEnable enables incoming contact requests\n\tContactRequestEnable(ctx context.Context, in *ContactRequestEnable_Request, opts ...grpc.CallOption) (*ContactRequestEnable_Reply, error)\n\t// ContactRequestResetReference changes the contact request reference\n\tContactRequestResetReference(ctx context.Context, in *ContactRequestResetReference_Request, opts ...grpc.CallOption) (*ContactRequestResetReference_Reply, error)\n\t// ContactRequestSend attempt to send a contact request\n\tContactRequestSend(ctx context.Context, in *ContactRequestSend_Request, opts ...grpc.CallOption) (*ContactRequestSend_Reply, error)\n\t// ContactRequestAccept accepts a contact request\n\tContactRequestAccept(ctx context.Context, in *ContactRequestAccept_Request, opts ...grpc.CallOption) (*ContactRequestAccept_Reply, error)\n\t// ContactRequestDiscard ignores a contact request, without informing the other user\n\tContactRequestDiscard(ctx context.Context, in *ContactRequestDiscard_Request, opts ...grpc.CallOption) (*ContactRequestDiscard_Reply, error)\n\t// ShareContact uses ContactRequestReference to get the contact information for the current account and\n\t// returns the Protobuf encoding of a shareable contact which you can further encode and share. If needed, this\n\t// will reset the contact request reference and enable contact requests. To decode the result, see DecodeContact.\n\tShareContact(ctx context.Context, in *ShareContact_Request, opts ...grpc.CallOption) (*ShareContact_Reply, error)\n\t// DecodeContact decodes the Protobuf encoding of a shareable contact which was returned by ShareContact.\n\tDecodeContact(ctx context.Context, in *DecodeContact_Request, opts ...grpc.CallOption) (*DecodeContact_Reply, error)\n\t// ContactBlock blocks a contact from sending requests\n\tContactBlock(ctx context.Context, in *ContactBlock_Request, opts ...grpc.CallOption) (*ContactBlock_Reply, error)\n\t// ContactUnblock unblocks a contact from sending requests\n\tContactUnblock(ctx context.Context, in *ContactUnblock_Request, opts ...grpc.CallOption) (*ContactUnblock_Reply, error)\n\t// ContactAliasKeySend send an alias key to a contact, the contact will be able to assert that your account is being present on a multi-member group\n\tContactAliasKeySend(ctx context.Context, in *ContactAliasKeySend_Request, opts ...grpc.CallOption) (*ContactAliasKeySend_Reply, error)\n\t// MultiMemberGroupCreate creates a new multi-member group\n\tMultiMemberGroupCreate(ctx context.Context, in *MultiMemberGroupCreate_Request, opts ...grpc.CallOption) (*MultiMemberGroupCreate_Reply, error)\n\t// MultiMemberGroupJoin joins a multi-member group\n\tMultiMemberGroupJoin(ctx context.Context, in *MultiMemberGroupJoin_Request, opts ...grpc.CallOption) (*MultiMemberGroupJoin_Reply, error)\n\t// MultiMemberGroupLeave leaves a multi-member group\n\tMultiMemberGroupLeave(ctx context.Context, in *MultiMemberGroupLeave_Request, opts ...grpc.CallOption) (*MultiMemberGroupLeave_Reply, error)\n\t// MultiMemberGroupAliasResolverDisclose discloses your alias resolver key\n\tMultiMemberGroupAliasResolverDisclose(ctx context.Context, in *MultiMemberGroupAliasResolverDisclose_Request, opts ...grpc.CallOption) (*MultiMemberGroupAliasResolverDisclose_Reply, error)\n\t// MultiMemberGroupAdminRoleGrant grants an admin role to a group member\n\tMultiMemberGroupAdminRoleGrant(ctx context.Context, in *MultiMemberGroupAdminRoleGrant_Request, opts ...grpc.CallOption) (*MultiMemberGroupAdminRoleGrant_Reply, error)\n\t// MultiMemberGroupInvitationCreate creates an invitation to a multi-member group\n\tMultiMemberGroupInvitationCreate(ctx context.Context, in *MultiMemberGroupInvitationCreate_Request, opts ...grpc.CallOption) (*MultiMemberGroupInvitationCreate_Reply, error)\n\t// AppMetadataSend adds an app event to the metadata store, the message is encrypted using a symmetric key and readable by future group members\n\tAppMetadataSend(ctx context.Context, in *AppMetadataSend_Request, opts ...grpc.CallOption) (*AppMetadataSend_Reply, error)\n\t// AppMessageSend adds an app event to the message store, the message is encrypted using a derived key and readable by current group members\n\tAppMessageSend(ctx context.Context, in *AppMessageSend_Request, opts ...grpc.CallOption) (*AppMessageSend_Reply, error)\n\t// GroupMetadataList replays previous and subscribes to new metadata events from the group\n\tGroupMetadataList(ctx context.Context, in *GroupMetadataList_Request, opts ...grpc.CallOption) (grpc.ServerStreamingClient[GroupMetadataEvent], error)\n\t// GroupMessageList replays previous and subscribes to new message events from the group\n\tGroupMessageList(ctx context.Context, in *GroupMessageList_Request, opts ...grpc.CallOption) (grpc.ServerStreamingClient[GroupMessageEvent], error)\n\t// GroupInfo retrieves information about a group\n\tGroupInfo(ctx context.Context, in *GroupInfo_Request, opts ...grpc.CallOption) (*GroupInfo_Reply, error)\n\t// ActivateGroup explicitly opens a group\n\tActivateGroup(ctx context.Context, in *ActivateGroup_Request, opts ...grpc.CallOption) (*ActivateGroup_Reply, error)\n\t// DeactivateGroup closes a group\n\tDeactivateGroup(ctx context.Context, in *DeactivateGroup_Request, opts ...grpc.CallOption) (*DeactivateGroup_Reply, error)\n\t// GroupDeviceStatus monitor device status\n\tGroupDeviceStatus(ctx context.Context, in *GroupDeviceStatus_Request, opts ...grpc.CallOption) (grpc.ServerStreamingClient[GroupDeviceStatus_Reply], error)\n\tDebugListGroups(ctx context.Context, in *DebugListGroups_Request, opts ...grpc.CallOption) (grpc.ServerStreamingClient[DebugListGroups_Reply], error)\n\tDebugInspectGroupStore(ctx context.Context, in *DebugInspectGroupStore_Request, opts ...grpc.CallOption) (grpc.ServerStreamingClient[DebugInspectGroupStore_Reply], error)\n\tDebugGroup(ctx context.Context, in *DebugGroup_Request, opts ...grpc.CallOption) (*DebugGroup_Reply, error)\n\tSystemInfo(ctx context.Context, in *SystemInfo_Request, opts ...grpc.CallOption) (*SystemInfo_Reply, error)\n\t// CredentialVerificationServiceInitFlow Initialize a credential verification flow\n\tCredentialVerificationServiceInitFlow(ctx context.Context, in *CredentialVerificationServiceInitFlow_Request, opts ...grpc.CallOption) (*CredentialVerificationServiceInitFlow_Reply, error)\n\t// CredentialVerificationServiceCompleteFlow Completes a credential verification flow\n\tCredentialVerificationServiceCompleteFlow(ctx context.Context, in *CredentialVerificationServiceCompleteFlow_Request, opts ...grpc.CallOption) (*CredentialVerificationServiceCompleteFlow_Reply, error)\n\t// VerifiedCredentialsList Retrieves the list of verified credentials\n\tVerifiedCredentialsList(ctx context.Context, in *VerifiedCredentialsList_Request, opts ...grpc.CallOption) (grpc.ServerStreamingClient[VerifiedCredentialsList_Reply], error)\n\t// ReplicationServiceRegisterGroup Asks a replication service to distribute a group contents\n\tReplicationServiceRegisterGroup(ctx context.Context, in *ReplicationServiceRegisterGroup_Request, opts ...grpc.CallOption) (*ReplicationServiceRegisterGroup_Reply, error)\n\t// PeerList returns a list of P2P peers\n\tPeerList(ctx context.Context, in *PeerList_Request, opts ...grpc.CallOption) (*PeerList_Reply, error)\n\t// OutOfStoreReceive parses a payload received outside a synchronized store\n\tOutOfStoreReceive(ctx context.Context, in *OutOfStoreReceive_Request, opts ...grpc.CallOption) (*OutOfStoreReceive_Reply, error)\n\t// OutOfStoreSeal creates a payload of a message present in store to be sent outside a synchronized store\n\tOutOfStoreSeal(ctx context.Context, in *OutOfStoreSeal_Request, opts ...grpc.CallOption) (*OutOfStoreSeal_Reply, error)\n\t// RefreshContactRequest try to refresh the contact request for the given contact\n\tRefreshContactRequest(ctx context.Context, in *RefreshContactRequest_Request, opts ...grpc.CallOption) (*RefreshContactRequest_Reply, error)\n}\n\ntype protocolServiceClient struct {\n\tcc grpc.ClientConnInterface\n}\n\nfunc NewProtocolServiceClient(cc grpc.ClientConnInterface) ProtocolServiceClient {\n\treturn &protocolServiceClient{cc}\n}\n\nfunc (c *protocolServiceClient) ServiceExportData(ctx context.Context, in *ServiceExportData_Request, opts ...grpc.CallOption) (grpc.ServerStreamingClient[ServiceExportData_Reply], error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tstream, err := c.cc.NewStream(ctx, &ProtocolService_ServiceDesc.Streams[0], ProtocolService_ServiceExportData_FullMethodName, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tx := &grpc.GenericClientStream[ServiceExportData_Request, ServiceExportData_Reply]{ClientStream: stream}\n\tif err := x.ClientStream.SendMsg(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif err := x.ClientStream.CloseSend(); err != nil {\n\t\treturn nil, err\n\t}\n\treturn x, nil\n}\n\n// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.\ntype ProtocolService_ServiceExportDataClient = grpc.ServerStreamingClient[ServiceExportData_Reply]\n\nfunc (c *protocolServiceClient) ServiceGetConfiguration(ctx context.Context, in *ServiceGetConfiguration_Request, opts ...grpc.CallOption) (*ServiceGetConfiguration_Reply, error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tout := new(ServiceGetConfiguration_Reply)\n\terr := c.cc.Invoke(ctx, ProtocolService_ServiceGetConfiguration_FullMethodName, in, out, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *protocolServiceClient) ContactRequestReference(ctx context.Context, in *ContactRequestReference_Request, opts ...grpc.CallOption) (*ContactRequestReference_Reply, error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tout := new(ContactRequestReference_Reply)\n\terr := c.cc.Invoke(ctx, ProtocolService_ContactRequestReference_FullMethodName, in, out, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *protocolServiceClient) ContactRequestDisable(ctx context.Context, in *ContactRequestDisable_Request, opts ...grpc.CallOption) (*ContactRequestDisable_Reply, error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tout := new(ContactRequestDisable_Reply)\n\terr := c.cc.Invoke(ctx, ProtocolService_ContactRequestDisable_FullMethodName, in, out, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *protocolServiceClient) ContactRequestEnable(ctx context.Context, in *ContactRequestEnable_Request, opts ...grpc.CallOption) (*ContactRequestEnable_Reply, error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tout := new(ContactRequestEnable_Reply)\n\terr := c.cc.Invoke(ctx, ProtocolService_ContactRequestEnable_FullMethodName, in, out, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *protocolServiceClient) ContactRequestResetReference(ctx context.Context, in *ContactRequestResetReference_Request, opts ...grpc.CallOption) (*ContactRequestResetReference_Reply, error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tout := new(ContactRequestResetReference_Reply)\n\terr := c.cc.Invoke(ctx, ProtocolService_ContactRequestResetReference_FullMethodName, in, out, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *protocolServiceClient) ContactRequestSend(ctx context.Context, in *ContactRequestSend_Request, opts ...grpc.CallOption) (*ContactRequestSend_Reply, error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tout := new(ContactRequestSend_Reply)\n\terr := c.cc.Invoke(ctx, ProtocolService_ContactRequestSend_FullMethodName, in, out, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *protocolServiceClient) ContactRequestAccept(ctx context.Context, in *ContactRequestAccept_Request, opts ...grpc.CallOption) (*ContactRequestAccept_Reply, error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tout := new(ContactRequestAccept_Reply)\n\terr := c.cc.Invoke(ctx, ProtocolService_ContactRequestAccept_FullMethodName, in, out, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *protocolServiceClient) ContactRequestDiscard(ctx context.Context, in *ContactRequestDiscard_Request, opts ...grpc.CallOption) (*ContactRequestDiscard_Reply, error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tout := new(ContactRequestDiscard_Reply)\n\terr := c.cc.Invoke(ctx, ProtocolService_ContactRequestDiscard_FullMethodName, in, out, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *protocolServiceClient) ShareContact(ctx context.Context, in *ShareContact_Request, opts ...grpc.CallOption) (*ShareContact_Reply, error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tout := new(ShareContact_Reply)\n\terr := c.cc.Invoke(ctx, ProtocolService_ShareContact_FullMethodName, in, out, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *protocolServiceClient) DecodeContact(ctx context.Context, in *DecodeContact_Request, opts ...grpc.CallOption) (*DecodeContact_Reply, error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tout := new(DecodeContact_Reply)\n\terr := c.cc.Invoke(ctx, ProtocolService_DecodeContact_FullMethodName, in, out, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *protocolServiceClient) ContactBlock(ctx context.Context, in *ContactBlock_Request, opts ...grpc.CallOption) (*ContactBlock_Reply, error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tout := new(ContactBlock_Reply)\n\terr := c.cc.Invoke(ctx, ProtocolService_ContactBlock_FullMethodName, in, out, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *protocolServiceClient) ContactUnblock(ctx context.Context, in *ContactUnblock_Request, opts ...grpc.CallOption) (*ContactUnblock_Reply, error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tout := new(ContactUnblock_Reply)\n\terr := c.cc.Invoke(ctx, ProtocolService_ContactUnblock_FullMethodName, in, out, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *protocolServiceClient) ContactAliasKeySend(ctx context.Context, in *ContactAliasKeySend_Request, opts ...grpc.CallOption) (*ContactAliasKeySend_Reply, error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tout := new(ContactAliasKeySend_Reply)\n\terr := c.cc.Invoke(ctx, ProtocolService_ContactAliasKeySend_FullMethodName, in, out, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *protocolServiceClient) MultiMemberGroupCreate(ctx context.Context, in *MultiMemberGroupCreate_Request, opts ...grpc.CallOption) (*MultiMemberGroupCreate_Reply, error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tout := new(MultiMemberGroupCreate_Reply)\n\terr := c.cc.Invoke(ctx, ProtocolService_MultiMemberGroupCreate_FullMethodName, in, out, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *protocolServiceClient) MultiMemberGroupJoin(ctx context.Context, in *MultiMemberGroupJoin_Request, opts ...grpc.CallOption) (*MultiMemberGroupJoin_Reply, error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tout := new(MultiMemberGroupJoin_Reply)\n\terr := c.cc.Invoke(ctx, ProtocolService_MultiMemberGroupJoin_FullMethodName, in, out, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *protocolServiceClient) MultiMemberGroupLeave(ctx context.Context, in *MultiMemberGroupLeave_Request, opts ...grpc.CallOption) (*MultiMemberGroupLeave_Reply, error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tout := new(MultiMemberGroupLeave_Reply)\n\terr := c.cc.Invoke(ctx, ProtocolService_MultiMemberGroupLeave_FullMethodName, in, out, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *protocolServiceClient) MultiMemberGroupAliasResolverDisclose(ctx context.Context, in *MultiMemberGroupAliasResolverDisclose_Request, opts ...grpc.CallOption) (*MultiMemberGroupAliasResolverDisclose_Reply, error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tout := new(MultiMemberGroupAliasResolverDisclose_Reply)\n\terr := c.cc.Invoke(ctx, ProtocolService_MultiMemberGroupAliasResolverDisclose_FullMethodName, in, out, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *protocolServiceClient) MultiMemberGroupAdminRoleGrant(ctx context.Context, in *MultiMemberGroupAdminRoleGrant_Request, opts ...grpc.CallOption) (*MultiMemberGroupAdminRoleGrant_Reply, error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tout := new(MultiMemberGroupAdminRoleGrant_Reply)\n\terr := c.cc.Invoke(ctx, ProtocolService_MultiMemberGroupAdminRoleGrant_FullMethodName, in, out, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *protocolServiceClient) MultiMemberGroupInvitationCreate(ctx context.Context, in *MultiMemberGroupInvitationCreate_Request, opts ...grpc.CallOption) (*MultiMemberGroupInvitationCreate_Reply, error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tout := new(MultiMemberGroupInvitationCreate_Reply)\n\terr := c.cc.Invoke(ctx, ProtocolService_MultiMemberGroupInvitationCreate_FullMethodName, in, out, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *protocolServiceClient) AppMetadataSend(ctx context.Context, in *AppMetadataSend_Request, opts ...grpc.CallOption) (*AppMetadataSend_Reply, error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tout := new(AppMetadataSend_Reply)\n\terr := c.cc.Invoke(ctx, ProtocolService_AppMetadataSend_FullMethodName, in, out, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *protocolServiceClient) AppMessageSend(ctx context.Context, in *AppMessageSend_Request, opts ...grpc.CallOption) (*AppMessageSend_Reply, error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tout := new(AppMessageSend_Reply)\n\terr := c.cc.Invoke(ctx, ProtocolService_AppMessageSend_FullMethodName, in, out, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *protocolServiceClient) GroupMetadataList(ctx context.Context, in *GroupMetadataList_Request, opts ...grpc.CallOption) (grpc.ServerStreamingClient[GroupMetadataEvent], error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tstream, err := c.cc.NewStream(ctx, &ProtocolService_ServiceDesc.Streams[1], ProtocolService_GroupMetadataList_FullMethodName, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tx := &grpc.GenericClientStream[GroupMetadataList_Request, GroupMetadataEvent]{ClientStream: stream}\n\tif err := x.ClientStream.SendMsg(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif err := x.ClientStream.CloseSend(); err != nil {\n\t\treturn nil, err\n\t}\n\treturn x, nil\n}\n\n// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.\ntype ProtocolService_GroupMetadataListClient = grpc.ServerStreamingClient[GroupMetadataEvent]\n\nfunc (c *protocolServiceClient) GroupMessageList(ctx context.Context, in *GroupMessageList_Request, opts ...grpc.CallOption) (grpc.ServerStreamingClient[GroupMessageEvent], error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tstream, err := c.cc.NewStream(ctx, &ProtocolService_ServiceDesc.Streams[2], ProtocolService_GroupMessageList_FullMethodName, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tx := &grpc.GenericClientStream[GroupMessageList_Request, GroupMessageEvent]{ClientStream: stream}\n\tif err := x.ClientStream.SendMsg(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif err := x.ClientStream.CloseSend(); err != nil {\n\t\treturn nil, err\n\t}\n\treturn x, nil\n}\n\n// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.\ntype ProtocolService_GroupMessageListClient = grpc.ServerStreamingClient[GroupMessageEvent]\n\nfunc (c *protocolServiceClient) GroupInfo(ctx context.Context, in *GroupInfo_Request, opts ...grpc.CallOption) (*GroupInfo_Reply, error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tout := new(GroupInfo_Reply)\n\terr := c.cc.Invoke(ctx, ProtocolService_GroupInfo_FullMethodName, in, out, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *protocolServiceClient) ActivateGroup(ctx context.Context, in *ActivateGroup_Request, opts ...grpc.CallOption) (*ActivateGroup_Reply, error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tout := new(ActivateGroup_Reply)\n\terr := c.cc.Invoke(ctx, ProtocolService_ActivateGroup_FullMethodName, in, out, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *protocolServiceClient) DeactivateGroup(ctx context.Context, in *DeactivateGroup_Request, opts ...grpc.CallOption) (*DeactivateGroup_Reply, error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tout := new(DeactivateGroup_Reply)\n\terr := c.cc.Invoke(ctx, ProtocolService_DeactivateGroup_FullMethodName, in, out, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *protocolServiceClient) GroupDeviceStatus(ctx context.Context, in *GroupDeviceStatus_Request, opts ...grpc.CallOption) (grpc.ServerStreamingClient[GroupDeviceStatus_Reply], error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tstream, err := c.cc.NewStream(ctx, &ProtocolService_ServiceDesc.Streams[3], ProtocolService_GroupDeviceStatus_FullMethodName, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tx := &grpc.GenericClientStream[GroupDeviceStatus_Request, GroupDeviceStatus_Reply]{ClientStream: stream}\n\tif err := x.ClientStream.SendMsg(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif err := x.ClientStream.CloseSend(); err != nil {\n\t\treturn nil, err\n\t}\n\treturn x, nil\n}\n\n// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.\ntype ProtocolService_GroupDeviceStatusClient = grpc.ServerStreamingClient[GroupDeviceStatus_Reply]\n\nfunc (c *protocolServiceClient) DebugListGroups(ctx context.Context, in *DebugListGroups_Request, opts ...grpc.CallOption) (grpc.ServerStreamingClient[DebugListGroups_Reply], error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tstream, err := c.cc.NewStream(ctx, &ProtocolService_ServiceDesc.Streams[4], ProtocolService_DebugListGroups_FullMethodName, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tx := &grpc.GenericClientStream[DebugListGroups_Request, DebugListGroups_Reply]{ClientStream: stream}\n\tif err := x.ClientStream.SendMsg(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif err := x.ClientStream.CloseSend(); err != nil {\n\t\treturn nil, err\n\t}\n\treturn x, nil\n}\n\n// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.\ntype ProtocolService_DebugListGroupsClient = grpc.ServerStreamingClient[DebugListGroups_Reply]\n\nfunc (c *protocolServiceClient) DebugInspectGroupStore(ctx context.Context, in *DebugInspectGroupStore_Request, opts ...grpc.CallOption) (grpc.ServerStreamingClient[DebugInspectGroupStore_Reply], error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tstream, err := c.cc.NewStream(ctx, &ProtocolService_ServiceDesc.Streams[5], ProtocolService_DebugInspectGroupStore_FullMethodName, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tx := &grpc.GenericClientStream[DebugInspectGroupStore_Request, DebugInspectGroupStore_Reply]{ClientStream: stream}\n\tif err := x.ClientStream.SendMsg(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif err := x.ClientStream.CloseSend(); err != nil {\n\t\treturn nil, err\n\t}\n\treturn x, nil\n}\n\n// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.\ntype ProtocolService_DebugInspectGroupStoreClient = grpc.ServerStreamingClient[DebugInspectGroupStore_Reply]\n\nfunc (c *protocolServiceClient) DebugGroup(ctx context.Context, in *DebugGroup_Request, opts ...grpc.CallOption) (*DebugGroup_Reply, error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tout := new(DebugGroup_Reply)\n\terr := c.cc.Invoke(ctx, ProtocolService_DebugGroup_FullMethodName, in, out, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *protocolServiceClient) SystemInfo(ctx context.Context, in *SystemInfo_Request, opts ...grpc.CallOption) (*SystemInfo_Reply, error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tout := new(SystemInfo_Reply)\n\terr := c.cc.Invoke(ctx, ProtocolService_SystemInfo_FullMethodName, in, out, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *protocolServiceClient) CredentialVerificationServiceInitFlow(ctx context.Context, in *CredentialVerificationServiceInitFlow_Request, opts ...grpc.CallOption) (*CredentialVerificationServiceInitFlow_Reply, error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tout := new(CredentialVerificationServiceInitFlow_Reply)\n\terr := c.cc.Invoke(ctx, ProtocolService_CredentialVerificationServiceInitFlow_FullMethodName, in, out, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *protocolServiceClient) CredentialVerificationServiceCompleteFlow(ctx context.Context, in *CredentialVerificationServiceCompleteFlow_Request, opts ...grpc.CallOption) (*CredentialVerificationServiceCompleteFlow_Reply, error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tout := new(CredentialVerificationServiceCompleteFlow_Reply)\n\terr := c.cc.Invoke(ctx, ProtocolService_CredentialVerificationServiceCompleteFlow_FullMethodName, in, out, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *protocolServiceClient) VerifiedCredentialsList(ctx context.Context, in *VerifiedCredentialsList_Request, opts ...grpc.CallOption) (grpc.ServerStreamingClient[VerifiedCredentialsList_Reply], error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tstream, err := c.cc.NewStream(ctx, &ProtocolService_ServiceDesc.Streams[6], ProtocolService_VerifiedCredentialsList_FullMethodName, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tx := &grpc.GenericClientStream[VerifiedCredentialsList_Request, VerifiedCredentialsList_Reply]{ClientStream: stream}\n\tif err := x.ClientStream.SendMsg(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif err := x.ClientStream.CloseSend(); err != nil {\n\t\treturn nil, err\n\t}\n\treturn x, nil\n}\n\n// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.\ntype ProtocolService_VerifiedCredentialsListClient = grpc.ServerStreamingClient[VerifiedCredentialsList_Reply]\n\nfunc (c *protocolServiceClient) ReplicationServiceRegisterGroup(ctx context.Context, in *ReplicationServiceRegisterGroup_Request, opts ...grpc.CallOption) (*ReplicationServiceRegisterGroup_Reply, error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tout := new(ReplicationServiceRegisterGroup_Reply)\n\terr := c.cc.Invoke(ctx, ProtocolService_ReplicationServiceRegisterGroup_FullMethodName, in, out, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *protocolServiceClient) PeerList(ctx context.Context, in *PeerList_Request, opts ...grpc.CallOption) (*PeerList_Reply, error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tout := new(PeerList_Reply)\n\terr := c.cc.Invoke(ctx, ProtocolService_PeerList_FullMethodName, in, out, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *protocolServiceClient) OutOfStoreReceive(ctx context.Context, in *OutOfStoreReceive_Request, opts ...grpc.CallOption) (*OutOfStoreReceive_Reply, error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tout := new(OutOfStoreReceive_Reply)\n\terr := c.cc.Invoke(ctx, ProtocolService_OutOfStoreReceive_FullMethodName, in, out, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *protocolServiceClient) OutOfStoreSeal(ctx context.Context, in *OutOfStoreSeal_Request, opts ...grpc.CallOption) (*OutOfStoreSeal_Reply, error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tout := new(OutOfStoreSeal_Reply)\n\terr := c.cc.Invoke(ctx, ProtocolService_OutOfStoreSeal_FullMethodName, in, out, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *protocolServiceClient) RefreshContactRequest(ctx context.Context, in *RefreshContactRequest_Request, opts ...grpc.CallOption) (*RefreshContactRequest_Reply, error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tout := new(RefreshContactRequest_Reply)\n\terr := c.cc.Invoke(ctx, ProtocolService_RefreshContactRequest_FullMethodName, in, out, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\n// ProtocolServiceServer is the server API for ProtocolService service.\n// All implementations must embed UnimplementedProtocolServiceServer\n// for forward compatibility.\n//\n// ProtocolService is the top-level API to manage the Wesh protocol service.\n// Each active Wesh protocol service is considered as a Wesh device and is associated with a Wesh user.\ntype ProtocolServiceServer interface {\n\t// ServiceExportData exports the current data of the protocol service\n\tServiceExportData(*ServiceExportData_Request, grpc.ServerStreamingServer[ServiceExportData_Reply]) error\n\t// ServiceGetConfiguration gets the current configuration of the protocol service\n\tServiceGetConfiguration(context.Context, *ServiceGetConfiguration_Request) (*ServiceGetConfiguration_Reply, error)\n\t// ContactRequestReference retrieves the information required to create a reference (ie. included in a shareable link) to the current account\n\tContactRequestReference(context.Context, *ContactRequestReference_Request) (*ContactRequestReference_Reply, error)\n\t// ContactRequestDisable disables incoming contact requests\n\tContactRequestDisable(context.Context, *ContactRequestDisable_Request) (*ContactRequestDisable_Reply, error)\n\t// ContactRequestEnable enables incoming contact requests\n\tContactRequestEnable(context.Context, *ContactRequestEnable_Request) (*ContactRequestEnable_Reply, error)\n\t// ContactRequestResetReference changes the contact request reference\n\tContactRequestResetReference(context.Context, *ContactRequestResetReference_Request) (*ContactRequestResetReference_Reply, error)\n\t// ContactRequestSend attempt to send a contact request\n\tContactRequestSend(context.Context, *ContactRequestSend_Request) (*ContactRequestSend_Reply, error)\n\t// ContactRequestAccept accepts a contact request\n\tContactRequestAccept(context.Context, *ContactRequestAccept_Request) (*ContactRequestAccept_Reply, error)\n\t// ContactRequestDiscard ignores a contact request, without informing the other user\n\tContactRequestDiscard(context.Context, *ContactRequestDiscard_Request) (*ContactRequestDiscard_Reply, error)\n\t// ShareContact uses ContactRequestReference to get the contact information for the current account and\n\t// returns the Protobuf encoding of a shareable contact which you can further encode and share. If needed, this\n\t// will reset the contact request reference and enable contact requests. To decode the result, see DecodeContact.\n\tShareContact(context.Context, *ShareContact_Request) (*ShareContact_Reply, error)\n\t// DecodeContact decodes the Protobuf encoding of a shareable contact which was returned by ShareContact.\n\tDecodeContact(context.Context, *DecodeContact_Request) (*DecodeContact_Reply, error)\n\t// ContactBlock blocks a contact from sending requests\n\tContactBlock(context.Context, *ContactBlock_Request) (*ContactBlock_Reply, error)\n\t// ContactUnblock unblocks a contact from sending requests\n\tContactUnblock(context.Context, *ContactUnblock_Request) (*ContactUnblock_Reply, error)\n\t// ContactAliasKeySend send an alias key to a contact, the contact will be able to assert that your account is being present on a multi-member group\n\tContactAliasKeySend(context.Context, *ContactAliasKeySend_Request) (*ContactAliasKeySend_Reply, error)\n\t// MultiMemberGroupCreate creates a new multi-member group\n\tMultiMemberGroupCreate(context.Context, *MultiMemberGroupCreate_Request) (*MultiMemberGroupCreate_Reply, error)\n\t// MultiMemberGroupJoin joins a multi-member group\n\tMultiMemberGroupJoin(context.Context, *MultiMemberGroupJoin_Request) (*MultiMemberGroupJoin_Reply, error)\n\t// MultiMemberGroupLeave leaves a multi-member group\n\tMultiMemberGroupLeave(context.Context, *MultiMemberGroupLeave_Request) (*MultiMemberGroupLeave_Reply, error)\n\t// MultiMemberGroupAliasResolverDisclose discloses your alias resolver key\n\tMultiMemberGroupAliasResolverDisclose(context.Context, *MultiMemberGroupAliasResolverDisclose_Request) (*MultiMemberGroupAliasResolverDisclose_Reply, error)\n\t// MultiMemberGroupAdminRoleGrant grants an admin role to a group member\n\tMultiMemberGroupAdminRoleGrant(context.Context, *MultiMemberGroupAdminRoleGrant_Request) (*MultiMemberGroupAdminRoleGrant_Reply, error)\n\t// MultiMemberGroupInvitationCreate creates an invitation to a multi-member group\n\tMultiMemberGroupInvitationCreate(context.Context, *MultiMemberGroupInvitationCreate_Request) (*MultiMemberGroupInvitationCreate_Reply, error)\n\t// AppMetadataSend adds an app event to the metadata store, the message is encrypted using a symmetric key and readable by future group members\n\tAppMetadataSend(context.Context, *AppMetadataSend_Request) (*AppMetadataSend_Reply, error)\n\t// AppMessageSend adds an app event to the message store, the message is encrypted using a derived key and readable by current group members\n\tAppMessageSend(context.Context, *AppMessageSend_Request) (*AppMessageSend_Reply, error)\n\t// GroupMetadataList replays previous and subscribes to new metadata events from the group\n\tGroupMetadataList(*GroupMetadataList_Request, grpc.ServerStreamingServer[GroupMetadataEvent]) error\n\t// GroupMessageList replays previous and subscribes to new message events from the group\n\tGroupMessageList(*GroupMessageList_Request, grpc.ServerStreamingServer[GroupMessageEvent]) error\n\t// GroupInfo retrieves information about a group\n\tGroupInfo(context.Context, *GroupInfo_Request) (*GroupInfo_Reply, error)\n\t// ActivateGroup explicitly opens a group\n\tActivateGroup(context.Context, *ActivateGroup_Request) (*ActivateGroup_Reply, error)\n\t// DeactivateGroup closes a group\n\tDeactivateGroup(context.Context, *DeactivateGroup_Request) (*DeactivateGroup_Reply, error)\n\t// GroupDeviceStatus monitor device status\n\tGroupDeviceStatus(*GroupDeviceStatus_Request, grpc.ServerStreamingServer[GroupDeviceStatus_Reply]) error\n\tDebugListGroups(*DebugListGroups_Request, grpc.ServerStreamingServer[DebugListGroups_Reply]) error\n\tDebugInspectGroupStore(*DebugInspectGroupStore_Request, grpc.ServerStreamingServer[DebugInspectGroupStore_Reply]) error\n\tDebugGroup(context.Context, *DebugGroup_Request) (*DebugGroup_Reply, error)\n\tSystemInfo(context.Context, *SystemInfo_Request) (*SystemInfo_Reply, error)\n\t// CredentialVerificationServiceInitFlow Initialize a credential verification flow\n\tCredentialVerificationServiceInitFlow(context.Context, *CredentialVerificationServiceInitFlow_Request) (*CredentialVerificationServiceInitFlow_Reply, error)\n\t// CredentialVerificationServiceCompleteFlow Completes a credential verification flow\n\tCredentialVerificationServiceCompleteFlow(context.Context, *CredentialVerificationServiceCompleteFlow_Request) (*CredentialVerificationServiceCompleteFlow_Reply, error)\n\t// VerifiedCredentialsList Retrieves the list of verified credentials\n\tVerifiedCredentialsList(*VerifiedCredentialsList_Request, grpc.ServerStreamingServer[VerifiedCredentialsList_Reply]) error\n\t// ReplicationServiceRegisterGroup Asks a replication service to distribute a group contents\n\tReplicationServiceRegisterGroup(context.Context, *ReplicationServiceRegisterGroup_Request) (*ReplicationServiceRegisterGroup_Reply, error)\n\t// PeerList returns a list of P2P peers\n\tPeerList(context.Context, *PeerList_Request) (*PeerList_Reply, error)\n\t// OutOfStoreReceive parses a payload received outside a synchronized store\n\tOutOfStoreReceive(context.Context, *OutOfStoreReceive_Request) (*OutOfStoreReceive_Reply, error)\n\t// OutOfStoreSeal creates a payload of a message present in store to be sent outside a synchronized store\n\tOutOfStoreSeal(context.Context, *OutOfStoreSeal_Request) (*OutOfStoreSeal_Reply, error)\n\t// RefreshContactRequest try to refresh the contact request for the given contact\n\tRefreshContactRequest(context.Context, *RefreshContactRequest_Request) (*RefreshContactRequest_Reply, error)\n\tmustEmbedUnimplementedProtocolServiceServer()\n}\n\n// UnimplementedProtocolServiceServer must be embedded to have\n// forward compatible implementations.\n//\n// NOTE: this should be embedded by value instead of pointer to avoid a nil\n// pointer dereference when methods are called.\ntype UnimplementedProtocolServiceServer struct{}\n\nfunc (UnimplementedProtocolServiceServer) ServiceExportData(*ServiceExportData_Request, grpc.ServerStreamingServer[ServiceExportData_Reply]) error {\n\treturn status.Errorf(codes.Unimplemented, \"method ServiceExportData not implemented\")\n}\nfunc (UnimplementedProtocolServiceServer) ServiceGetConfiguration(context.Context, *ServiceGetConfiguration_Request) (*ServiceGetConfiguration_Reply, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method ServiceGetConfiguration not implemented\")\n}\nfunc (UnimplementedProtocolServiceServer) ContactRequestReference(context.Context, *ContactRequestReference_Request) (*ContactRequestReference_Reply, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method ContactRequestReference not implemented\")\n}\nfunc (UnimplementedProtocolServiceServer) ContactRequestDisable(context.Context, *ContactRequestDisable_Request) (*ContactRequestDisable_Reply, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method ContactRequestDisable not implemented\")\n}\nfunc (UnimplementedProtocolServiceServer) ContactRequestEnable(context.Context, *ContactRequestEnable_Request) (*ContactRequestEnable_Reply, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method ContactRequestEnable not implemented\")\n}\nfunc (UnimplementedProtocolServiceServer) ContactRequestResetReference(context.Context, *ContactRequestResetReference_Request) (*ContactRequestResetReference_Reply, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method ContactRequestResetReference not implemented\")\n}\nfunc (UnimplementedProtocolServiceServer) ContactRequestSend(context.Context, *ContactRequestSend_Request) (*ContactRequestSend_Reply, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method ContactRequestSend not implemented\")\n}\nfunc (UnimplementedProtocolServiceServer) ContactRequestAccept(context.Context, *ContactRequestAccept_Request) (*ContactRequestAccept_Reply, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method ContactRequestAccept not implemented\")\n}\nfunc (UnimplementedProtocolServiceServer) ContactRequestDiscard(context.Context, *ContactRequestDiscard_Request) (*ContactRequestDiscard_Reply, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method ContactRequestDiscard not implemented\")\n}\nfunc (UnimplementedProtocolServiceServer) ShareContact(context.Context, *ShareContact_Request) (*ShareContact_Reply, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method ShareContact not implemented\")\n}\nfunc (UnimplementedProtocolServiceServer) DecodeContact(context.Context, *DecodeContact_Request) (*DecodeContact_Reply, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method DecodeContact not implemented\")\n}\nfunc (UnimplementedProtocolServiceServer) ContactBlock(context.Context, *ContactBlock_Request) (*ContactBlock_Reply, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method ContactBlock not implemented\")\n}\nfunc (UnimplementedProtocolServiceServer) ContactUnblock(context.Context, *ContactUnblock_Request) (*ContactUnblock_Reply, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method ContactUnblock not implemented\")\n}\nfunc (UnimplementedProtocolServiceServer) ContactAliasKeySend(context.Context, *ContactAliasKeySend_Request) (*ContactAliasKeySend_Reply, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method ContactAliasKeySend not implemented\")\n}\nfunc (UnimplementedProtocolServiceServer) MultiMemberGroupCreate(context.Context, *MultiMemberGroupCreate_Request) (*MultiMemberGroupCreate_Reply, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method MultiMemberGroupCreate not implemented\")\n}\nfunc (UnimplementedProtocolServiceServer) MultiMemberGroupJoin(context.Context, *MultiMemberGroupJoin_Request) (*MultiMemberGroupJoin_Reply, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method MultiMemberGroupJoin not implemented\")\n}\nfunc (UnimplementedProtocolServiceServer) MultiMemberGroupLeave(context.Context, *MultiMemberGroupLeave_Request) (*MultiMemberGroupLeave_Reply, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method MultiMemberGroupLeave not implemented\")\n}\nfunc (UnimplementedProtocolServiceServer) MultiMemberGroupAliasResolverDisclose(context.Context, *MultiMemberGroupAliasResolverDisclose_Request) (*MultiMemberGroupAliasResolverDisclose_Reply, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method MultiMemberGroupAliasResolverDisclose not implemented\")\n}\nfunc (UnimplementedProtocolServiceServer) MultiMemberGroupAdminRoleGrant(context.Context, *MultiMemberGroupAdminRoleGrant_Request) (*MultiMemberGroupAdminRoleGrant_Reply, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method MultiMemberGroupAdminRoleGrant not implemented\")\n}\nfunc (UnimplementedProtocolServiceServer) MultiMemberGroupInvitationCreate(context.Context, *MultiMemberGroupInvitationCreate_Request) (*MultiMemberGroupInvitationCreate_Reply, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method MultiMemberGroupInvitationCreate not implemented\")\n}\nfunc (UnimplementedProtocolServiceServer) AppMetadataSend(context.Context, *AppMetadataSend_Request) (*AppMetadataSend_Reply, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method AppMetadataSend not implemented\")\n}\nfunc (UnimplementedProtocolServiceServer) AppMessageSend(context.Context, *AppMessageSend_Request) (*AppMessageSend_Reply, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method AppMessageSend not implemented\")\n}\nfunc (UnimplementedProtocolServiceServer) GroupMetadataList(*GroupMetadataList_Request, grpc.ServerStreamingServer[GroupMetadataEvent]) error {\n\treturn status.Errorf(codes.Unimplemented, \"method GroupMetadataList not implemented\")\n}\nfunc (UnimplementedProtocolServiceServer) GroupMessageList(*GroupMessageList_Request, grpc.ServerStreamingServer[GroupMessageEvent]) error {\n\treturn status.Errorf(codes.Unimplemented, \"method GroupMessageList not implemented\")\n}\nfunc (UnimplementedProtocolServiceServer) GroupInfo(context.Context, *GroupInfo_Request) (*GroupInfo_Reply, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method GroupInfo not implemented\")\n}\nfunc (UnimplementedProtocolServiceServer) ActivateGroup(context.Context, *ActivateGroup_Request) (*ActivateGroup_Reply, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method ActivateGroup not implemented\")\n}\nfunc (UnimplementedProtocolServiceServer) DeactivateGroup(context.Context, *DeactivateGroup_Request) (*DeactivateGroup_Reply, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method DeactivateGroup not implemented\")\n}\nfunc (UnimplementedProtocolServiceServer) GroupDeviceStatus(*GroupDeviceStatus_Request, grpc.ServerStreamingServer[GroupDeviceStatus_Reply]) error {\n\treturn status.Errorf(codes.Unimplemented, \"method GroupDeviceStatus not implemented\")\n}\nfunc (UnimplementedProtocolServiceServer) DebugListGroups(*DebugListGroups_Request, grpc.ServerStreamingServer[DebugListGroups_Reply]) error {\n\treturn status.Errorf(codes.Unimplemented, \"method DebugListGroups not implemented\")\n}\nfunc (UnimplementedProtocolServiceServer) DebugInspectGroupStore(*DebugInspectGroupStore_Request, grpc.ServerStreamingServer[DebugInspectGroupStore_Reply]) error {\n\treturn status.Errorf(codes.Unimplemented, \"method DebugInspectGroupStore not implemented\")\n}\nfunc (UnimplementedProtocolServiceServer) DebugGroup(context.Context, *DebugGroup_Request) (*DebugGroup_Reply, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method DebugGroup not implemented\")\n}\nfunc (UnimplementedProtocolServiceServer) SystemInfo(context.Context, *SystemInfo_Request) (*SystemInfo_Reply, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method SystemInfo not implemented\")\n}\nfunc (UnimplementedProtocolServiceServer) CredentialVerificationServiceInitFlow(context.Context, *CredentialVerificationServiceInitFlow_Request) (*CredentialVerificationServiceInitFlow_Reply, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method CredentialVerificationServiceInitFlow not implemented\")\n}\nfunc (UnimplementedProtocolServiceServer) CredentialVerificationServiceCompleteFlow(context.Context, *CredentialVerificationServiceCompleteFlow_Request) (*CredentialVerificationServiceCompleteFlow_Reply, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method CredentialVerificationServiceCompleteFlow not implemented\")\n}\nfunc (UnimplementedProtocolServiceServer) VerifiedCredentialsList(*VerifiedCredentialsList_Request, grpc.ServerStreamingServer[VerifiedCredentialsList_Reply]) error {\n\treturn status.Errorf(codes.Unimplemented, \"method VerifiedCredentialsList not implemented\")\n}\nfunc (UnimplementedProtocolServiceServer) ReplicationServiceRegisterGroup(context.Context, *ReplicationServiceRegisterGroup_Request) (*ReplicationServiceRegisterGroup_Reply, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method ReplicationServiceRegisterGroup not implemented\")\n}\nfunc (UnimplementedProtocolServiceServer) PeerList(context.Context, *PeerList_Request) (*PeerList_Reply, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method PeerList not implemented\")\n}\nfunc (UnimplementedProtocolServiceServer) OutOfStoreReceive(context.Context, *OutOfStoreReceive_Request) (*OutOfStoreReceive_Reply, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method OutOfStoreReceive not implemented\")\n}\nfunc (UnimplementedProtocolServiceServer) OutOfStoreSeal(context.Context, *OutOfStoreSeal_Request) (*OutOfStoreSeal_Reply, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method OutOfStoreSeal not implemented\")\n}\nfunc (UnimplementedProtocolServiceServer) RefreshContactRequest(context.Context, *RefreshContactRequest_Request) (*RefreshContactRequest_Reply, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method RefreshContactRequest not implemented\")\n}\nfunc (UnimplementedProtocolServiceServer) mustEmbedUnimplementedProtocolServiceServer() {}\nfunc (UnimplementedProtocolServiceServer) testEmbeddedByValue()                         {}\n\n// UnsafeProtocolServiceServer may be embedded to opt out of forward compatibility for this service.\n// Use of this interface is not recommended, as added methods to ProtocolServiceServer will\n// result in compilation errors.\ntype UnsafeProtocolServiceServer interface {\n\tmustEmbedUnimplementedProtocolServiceServer()\n}\n\nfunc RegisterProtocolServiceServer(s grpc.ServiceRegistrar, srv ProtocolServiceServer) {\n\t// If the following call pancis, it indicates UnimplementedProtocolServiceServer was\n\t// embedded by pointer and is nil.  This will cause panics if an\n\t// unimplemented method is ever invoked, so we test this at initialization\n\t// time to prevent it from happening at runtime later due to I/O.\n\tif t, ok := srv.(interface{ testEmbeddedByValue() }); ok {\n\t\tt.testEmbeddedByValue()\n\t}\n\ts.RegisterService(&ProtocolService_ServiceDesc, srv)\n}\n\nfunc _ProtocolService_ServiceExportData_Handler(srv interface{}, stream grpc.ServerStream) error {\n\tm := new(ServiceExportData_Request)\n\tif err := stream.RecvMsg(m); err != nil {\n\t\treturn err\n\t}\n\treturn srv.(ProtocolServiceServer).ServiceExportData(m, &grpc.GenericServerStream[ServiceExportData_Request, ServiceExportData_Reply]{ServerStream: stream})\n}\n\n// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.\ntype ProtocolService_ServiceExportDataServer = grpc.ServerStreamingServer[ServiceExportData_Reply]\n\nfunc _ProtocolService_ServiceGetConfiguration_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ServiceGetConfiguration_Request)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(ProtocolServiceServer).ServiceGetConfiguration(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: ProtocolService_ServiceGetConfiguration_FullMethodName,\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(ProtocolServiceServer).ServiceGetConfiguration(ctx, req.(*ServiceGetConfiguration_Request))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _ProtocolService_ContactRequestReference_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ContactRequestReference_Request)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(ProtocolServiceServer).ContactRequestReference(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: ProtocolService_ContactRequestReference_FullMethodName,\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(ProtocolServiceServer).ContactRequestReference(ctx, req.(*ContactRequestReference_Request))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _ProtocolService_ContactRequestDisable_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ContactRequestDisable_Request)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(ProtocolServiceServer).ContactRequestDisable(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: ProtocolService_ContactRequestDisable_FullMethodName,\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(ProtocolServiceServer).ContactRequestDisable(ctx, req.(*ContactRequestDisable_Request))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _ProtocolService_ContactRequestEnable_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ContactRequestEnable_Request)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(ProtocolServiceServer).ContactRequestEnable(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: ProtocolService_ContactRequestEnable_FullMethodName,\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(ProtocolServiceServer).ContactRequestEnable(ctx, req.(*ContactRequestEnable_Request))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _ProtocolService_ContactRequestResetReference_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ContactRequestResetReference_Request)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(ProtocolServiceServer).ContactRequestResetReference(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: ProtocolService_ContactRequestResetReference_FullMethodName,\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(ProtocolServiceServer).ContactRequestResetReference(ctx, req.(*ContactRequestResetReference_Request))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _ProtocolService_ContactRequestSend_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ContactRequestSend_Request)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(ProtocolServiceServer).ContactRequestSend(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: ProtocolService_ContactRequestSend_FullMethodName,\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(ProtocolServiceServer).ContactRequestSend(ctx, req.(*ContactRequestSend_Request))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _ProtocolService_ContactRequestAccept_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ContactRequestAccept_Request)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(ProtocolServiceServer).ContactRequestAccept(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: ProtocolService_ContactRequestAccept_FullMethodName,\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(ProtocolServiceServer).ContactRequestAccept(ctx, req.(*ContactRequestAccept_Request))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _ProtocolService_ContactRequestDiscard_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ContactRequestDiscard_Request)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(ProtocolServiceServer).ContactRequestDiscard(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: ProtocolService_ContactRequestDiscard_FullMethodName,\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(ProtocolServiceServer).ContactRequestDiscard(ctx, req.(*ContactRequestDiscard_Request))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _ProtocolService_ShareContact_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ShareContact_Request)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(ProtocolServiceServer).ShareContact(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: ProtocolService_ShareContact_FullMethodName,\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(ProtocolServiceServer).ShareContact(ctx, req.(*ShareContact_Request))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _ProtocolService_DecodeContact_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(DecodeContact_Request)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(ProtocolServiceServer).DecodeContact(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: ProtocolService_DecodeContact_FullMethodName,\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(ProtocolServiceServer).DecodeContact(ctx, req.(*DecodeContact_Request))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _ProtocolService_ContactBlock_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ContactBlock_Request)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(ProtocolServiceServer).ContactBlock(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: ProtocolService_ContactBlock_FullMethodName,\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(ProtocolServiceServer).ContactBlock(ctx, req.(*ContactBlock_Request))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _ProtocolService_ContactUnblock_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ContactUnblock_Request)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(ProtocolServiceServer).ContactUnblock(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: ProtocolService_ContactUnblock_FullMethodName,\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(ProtocolServiceServer).ContactUnblock(ctx, req.(*ContactUnblock_Request))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _ProtocolService_ContactAliasKeySend_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ContactAliasKeySend_Request)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(ProtocolServiceServer).ContactAliasKeySend(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: ProtocolService_ContactAliasKeySend_FullMethodName,\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(ProtocolServiceServer).ContactAliasKeySend(ctx, req.(*ContactAliasKeySend_Request))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _ProtocolService_MultiMemberGroupCreate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(MultiMemberGroupCreate_Request)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(ProtocolServiceServer).MultiMemberGroupCreate(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: ProtocolService_MultiMemberGroupCreate_FullMethodName,\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(ProtocolServiceServer).MultiMemberGroupCreate(ctx, req.(*MultiMemberGroupCreate_Request))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _ProtocolService_MultiMemberGroupJoin_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(MultiMemberGroupJoin_Request)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(ProtocolServiceServer).MultiMemberGroupJoin(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: ProtocolService_MultiMemberGroupJoin_FullMethodName,\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(ProtocolServiceServer).MultiMemberGroupJoin(ctx, req.(*MultiMemberGroupJoin_Request))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _ProtocolService_MultiMemberGroupLeave_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(MultiMemberGroupLeave_Request)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(ProtocolServiceServer).MultiMemberGroupLeave(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: ProtocolService_MultiMemberGroupLeave_FullMethodName,\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(ProtocolServiceServer).MultiMemberGroupLeave(ctx, req.(*MultiMemberGroupLeave_Request))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _ProtocolService_MultiMemberGroupAliasResolverDisclose_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(MultiMemberGroupAliasResolverDisclose_Request)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(ProtocolServiceServer).MultiMemberGroupAliasResolverDisclose(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: ProtocolService_MultiMemberGroupAliasResolverDisclose_FullMethodName,\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(ProtocolServiceServer).MultiMemberGroupAliasResolverDisclose(ctx, req.(*MultiMemberGroupAliasResolverDisclose_Request))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _ProtocolService_MultiMemberGroupAdminRoleGrant_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(MultiMemberGroupAdminRoleGrant_Request)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(ProtocolServiceServer).MultiMemberGroupAdminRoleGrant(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: ProtocolService_MultiMemberGroupAdminRoleGrant_FullMethodName,\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(ProtocolServiceServer).MultiMemberGroupAdminRoleGrant(ctx, req.(*MultiMemberGroupAdminRoleGrant_Request))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _ProtocolService_MultiMemberGroupInvitationCreate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(MultiMemberGroupInvitationCreate_Request)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(ProtocolServiceServer).MultiMemberGroupInvitationCreate(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: ProtocolService_MultiMemberGroupInvitationCreate_FullMethodName,\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(ProtocolServiceServer).MultiMemberGroupInvitationCreate(ctx, req.(*MultiMemberGroupInvitationCreate_Request))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _ProtocolService_AppMetadataSend_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(AppMetadataSend_Request)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(ProtocolServiceServer).AppMetadataSend(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: ProtocolService_AppMetadataSend_FullMethodName,\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(ProtocolServiceServer).AppMetadataSend(ctx, req.(*AppMetadataSend_Request))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _ProtocolService_AppMessageSend_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(AppMessageSend_Request)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(ProtocolServiceServer).AppMessageSend(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: ProtocolService_AppMessageSend_FullMethodName,\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(ProtocolServiceServer).AppMessageSend(ctx, req.(*AppMessageSend_Request))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _ProtocolService_GroupMetadataList_Handler(srv interface{}, stream grpc.ServerStream) error {\n\tm := new(GroupMetadataList_Request)\n\tif err := stream.RecvMsg(m); err != nil {\n\t\treturn err\n\t}\n\treturn srv.(ProtocolServiceServer).GroupMetadataList(m, &grpc.GenericServerStream[GroupMetadataList_Request, GroupMetadataEvent]{ServerStream: stream})\n}\n\n// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.\ntype ProtocolService_GroupMetadataListServer = grpc.ServerStreamingServer[GroupMetadataEvent]\n\nfunc _ProtocolService_GroupMessageList_Handler(srv interface{}, stream grpc.ServerStream) error {\n\tm := new(GroupMessageList_Request)\n\tif err := stream.RecvMsg(m); err != nil {\n\t\treturn err\n\t}\n\treturn srv.(ProtocolServiceServer).GroupMessageList(m, &grpc.GenericServerStream[GroupMessageList_Request, GroupMessageEvent]{ServerStream: stream})\n}\n\n// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.\ntype ProtocolService_GroupMessageListServer = grpc.ServerStreamingServer[GroupMessageEvent]\n\nfunc _ProtocolService_GroupInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(GroupInfo_Request)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(ProtocolServiceServer).GroupInfo(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: ProtocolService_GroupInfo_FullMethodName,\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(ProtocolServiceServer).GroupInfo(ctx, req.(*GroupInfo_Request))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _ProtocolService_ActivateGroup_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ActivateGroup_Request)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(ProtocolServiceServer).ActivateGroup(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: ProtocolService_ActivateGroup_FullMethodName,\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(ProtocolServiceServer).ActivateGroup(ctx, req.(*ActivateGroup_Request))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _ProtocolService_DeactivateGroup_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(DeactivateGroup_Request)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(ProtocolServiceServer).DeactivateGroup(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: ProtocolService_DeactivateGroup_FullMethodName,\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(ProtocolServiceServer).DeactivateGroup(ctx, req.(*DeactivateGroup_Request))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _ProtocolService_GroupDeviceStatus_Handler(srv interface{}, stream grpc.ServerStream) error {\n\tm := new(GroupDeviceStatus_Request)\n\tif err := stream.RecvMsg(m); err != nil {\n\t\treturn err\n\t}\n\treturn srv.(ProtocolServiceServer).GroupDeviceStatus(m, &grpc.GenericServerStream[GroupDeviceStatus_Request, GroupDeviceStatus_Reply]{ServerStream: stream})\n}\n\n// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.\ntype ProtocolService_GroupDeviceStatusServer = grpc.ServerStreamingServer[GroupDeviceStatus_Reply]\n\nfunc _ProtocolService_DebugListGroups_Handler(srv interface{}, stream grpc.ServerStream) error {\n\tm := new(DebugListGroups_Request)\n\tif err := stream.RecvMsg(m); err != nil {\n\t\treturn err\n\t}\n\treturn srv.(ProtocolServiceServer).DebugListGroups(m, &grpc.GenericServerStream[DebugListGroups_Request, DebugListGroups_Reply]{ServerStream: stream})\n}\n\n// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.\ntype ProtocolService_DebugListGroupsServer = grpc.ServerStreamingServer[DebugListGroups_Reply]\n\nfunc _ProtocolService_DebugInspectGroupStore_Handler(srv interface{}, stream grpc.ServerStream) error {\n\tm := new(DebugInspectGroupStore_Request)\n\tif err := stream.RecvMsg(m); err != nil {\n\t\treturn err\n\t}\n\treturn srv.(ProtocolServiceServer).DebugInspectGroupStore(m, &grpc.GenericServerStream[DebugInspectGroupStore_Request, DebugInspectGroupStore_Reply]{ServerStream: stream})\n}\n\n// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.\ntype ProtocolService_DebugInspectGroupStoreServer = grpc.ServerStreamingServer[DebugInspectGroupStore_Reply]\n\nfunc _ProtocolService_DebugGroup_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(DebugGroup_Request)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(ProtocolServiceServer).DebugGroup(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: ProtocolService_DebugGroup_FullMethodName,\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(ProtocolServiceServer).DebugGroup(ctx, req.(*DebugGroup_Request))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _ProtocolService_SystemInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(SystemInfo_Request)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(ProtocolServiceServer).SystemInfo(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: ProtocolService_SystemInfo_FullMethodName,\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(ProtocolServiceServer).SystemInfo(ctx, req.(*SystemInfo_Request))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _ProtocolService_CredentialVerificationServiceInitFlow_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(CredentialVerificationServiceInitFlow_Request)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(ProtocolServiceServer).CredentialVerificationServiceInitFlow(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: ProtocolService_CredentialVerificationServiceInitFlow_FullMethodName,\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(ProtocolServiceServer).CredentialVerificationServiceInitFlow(ctx, req.(*CredentialVerificationServiceInitFlow_Request))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _ProtocolService_CredentialVerificationServiceCompleteFlow_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(CredentialVerificationServiceCompleteFlow_Request)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(ProtocolServiceServer).CredentialVerificationServiceCompleteFlow(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: ProtocolService_CredentialVerificationServiceCompleteFlow_FullMethodName,\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(ProtocolServiceServer).CredentialVerificationServiceCompleteFlow(ctx, req.(*CredentialVerificationServiceCompleteFlow_Request))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _ProtocolService_VerifiedCredentialsList_Handler(srv interface{}, stream grpc.ServerStream) error {\n\tm := new(VerifiedCredentialsList_Request)\n\tif err := stream.RecvMsg(m); err != nil {\n\t\treturn err\n\t}\n\treturn srv.(ProtocolServiceServer).VerifiedCredentialsList(m, &grpc.GenericServerStream[VerifiedCredentialsList_Request, VerifiedCredentialsList_Reply]{ServerStream: stream})\n}\n\n// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.\ntype ProtocolService_VerifiedCredentialsListServer = grpc.ServerStreamingServer[VerifiedCredentialsList_Reply]\n\nfunc _ProtocolService_ReplicationServiceRegisterGroup_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ReplicationServiceRegisterGroup_Request)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(ProtocolServiceServer).ReplicationServiceRegisterGroup(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: ProtocolService_ReplicationServiceRegisterGroup_FullMethodName,\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(ProtocolServiceServer).ReplicationServiceRegisterGroup(ctx, req.(*ReplicationServiceRegisterGroup_Request))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _ProtocolService_PeerList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(PeerList_Request)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(ProtocolServiceServer).PeerList(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: ProtocolService_PeerList_FullMethodName,\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(ProtocolServiceServer).PeerList(ctx, req.(*PeerList_Request))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _ProtocolService_OutOfStoreReceive_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(OutOfStoreReceive_Request)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(ProtocolServiceServer).OutOfStoreReceive(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: ProtocolService_OutOfStoreReceive_FullMethodName,\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(ProtocolServiceServer).OutOfStoreReceive(ctx, req.(*OutOfStoreReceive_Request))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _ProtocolService_OutOfStoreSeal_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(OutOfStoreSeal_Request)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(ProtocolServiceServer).OutOfStoreSeal(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: ProtocolService_OutOfStoreSeal_FullMethodName,\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(ProtocolServiceServer).OutOfStoreSeal(ctx, req.(*OutOfStoreSeal_Request))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _ProtocolService_RefreshContactRequest_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(RefreshContactRequest_Request)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(ProtocolServiceServer).RefreshContactRequest(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: ProtocolService_RefreshContactRequest_FullMethodName,\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(ProtocolServiceServer).RefreshContactRequest(ctx, req.(*RefreshContactRequest_Request))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\n// ProtocolService_ServiceDesc is the grpc.ServiceDesc for ProtocolService service.\n// It's only intended for direct use with grpc.RegisterService,\n// and not to be introspected or modified (even as a copy)\nvar ProtocolService_ServiceDesc = grpc.ServiceDesc{\n\tServiceName: \"weshnet.protocol.v1.ProtocolService\",\n\tHandlerType: (*ProtocolServiceServer)(nil),\n\tMethods: []grpc.MethodDesc{\n\t\t{\n\t\t\tMethodName: \"ServiceGetConfiguration\",\n\t\t\tHandler:    _ProtocolService_ServiceGetConfiguration_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"ContactRequestReference\",\n\t\t\tHandler:    _ProtocolService_ContactRequestReference_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"ContactRequestDisable\",\n\t\t\tHandler:    _ProtocolService_ContactRequestDisable_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"ContactRequestEnable\",\n\t\t\tHandler:    _ProtocolService_ContactRequestEnable_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"ContactRequestResetReference\",\n\t\t\tHandler:    _ProtocolService_ContactRequestResetReference_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"ContactRequestSend\",\n\t\t\tHandler:    _ProtocolService_ContactRequestSend_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"ContactRequestAccept\",\n\t\t\tHandler:    _ProtocolService_ContactRequestAccept_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"ContactRequestDiscard\",\n\t\t\tHandler:    _ProtocolService_ContactRequestDiscard_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"ShareContact\",\n\t\t\tHandler:    _ProtocolService_ShareContact_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"DecodeContact\",\n\t\t\tHandler:    _ProtocolService_DecodeContact_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"ContactBlock\",\n\t\t\tHandler:    _ProtocolService_ContactBlock_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"ContactUnblock\",\n\t\t\tHandler:    _ProtocolService_ContactUnblock_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"ContactAliasKeySend\",\n\t\t\tHandler:    _ProtocolService_ContactAliasKeySend_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"MultiMemberGroupCreate\",\n\t\t\tHandler:    _ProtocolService_MultiMemberGroupCreate_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"MultiMemberGroupJoin\",\n\t\t\tHandler:    _ProtocolService_MultiMemberGroupJoin_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"MultiMemberGroupLeave\",\n\t\t\tHandler:    _ProtocolService_MultiMemberGroupLeave_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"MultiMemberGroupAliasResolverDisclose\",\n\t\t\tHandler:    _ProtocolService_MultiMemberGroupAliasResolverDisclose_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"MultiMemberGroupAdminRoleGrant\",\n\t\t\tHandler:    _ProtocolService_MultiMemberGroupAdminRoleGrant_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"MultiMemberGroupInvitationCreate\",\n\t\t\tHandler:    _ProtocolService_MultiMemberGroupInvitationCreate_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"AppMetadataSend\",\n\t\t\tHandler:    _ProtocolService_AppMetadataSend_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"AppMessageSend\",\n\t\t\tHandler:    _ProtocolService_AppMessageSend_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"GroupInfo\",\n\t\t\tHandler:    _ProtocolService_GroupInfo_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"ActivateGroup\",\n\t\t\tHandler:    _ProtocolService_ActivateGroup_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"DeactivateGroup\",\n\t\t\tHandler:    _ProtocolService_DeactivateGroup_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"DebugGroup\",\n\t\t\tHandler:    _ProtocolService_DebugGroup_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"SystemInfo\",\n\t\t\tHandler:    _ProtocolService_SystemInfo_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"CredentialVerificationServiceInitFlow\",\n\t\t\tHandler:    _ProtocolService_CredentialVerificationServiceInitFlow_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"CredentialVerificationServiceCompleteFlow\",\n\t\t\tHandler:    _ProtocolService_CredentialVerificationServiceCompleteFlow_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"ReplicationServiceRegisterGroup\",\n\t\t\tHandler:    _ProtocolService_ReplicationServiceRegisterGroup_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"PeerList\",\n\t\t\tHandler:    _ProtocolService_PeerList_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"OutOfStoreReceive\",\n\t\t\tHandler:    _ProtocolService_OutOfStoreReceive_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"OutOfStoreSeal\",\n\t\t\tHandler:    _ProtocolService_OutOfStoreSeal_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"RefreshContactRequest\",\n\t\t\tHandler:    _ProtocolService_RefreshContactRequest_Handler,\n\t\t},\n\t},\n\tStreams: []grpc.StreamDesc{\n\t\t{\n\t\t\tStreamName:    \"ServiceExportData\",\n\t\t\tHandler:       _ProtocolService_ServiceExportData_Handler,\n\t\t\tServerStreams: true,\n\t\t},\n\t\t{\n\t\t\tStreamName:    \"GroupMetadataList\",\n\t\t\tHandler:       _ProtocolService_GroupMetadataList_Handler,\n\t\t\tServerStreams: true,\n\t\t},\n\t\t{\n\t\t\tStreamName:    \"GroupMessageList\",\n\t\t\tHandler:       _ProtocolService_GroupMessageList_Handler,\n\t\t\tServerStreams: true,\n\t\t},\n\t\t{\n\t\t\tStreamName:    \"GroupDeviceStatus\",\n\t\t\tHandler:       _ProtocolService_GroupDeviceStatus_Handler,\n\t\t\tServerStreams: true,\n\t\t},\n\t\t{\n\t\t\tStreamName:    \"DebugListGroups\",\n\t\t\tHandler:       _ProtocolService_DebugListGroups_Handler,\n\t\t\tServerStreams: true,\n\t\t},\n\t\t{\n\t\t\tStreamName:    \"DebugInspectGroupStore\",\n\t\t\tHandler:       _ProtocolService_DebugInspectGroupStore_Handler,\n\t\t\tServerStreams: true,\n\t\t},\n\t\t{\n\t\t\tStreamName:    \"VerifiedCredentialsList\",\n\t\t\tHandler:       _ProtocolService_VerifiedCredentialsList_Handler,\n\t\t\tServerStreams: true,\n\t\t},\n\t},\n\tMetadata: \"protocoltypes.proto\",\n}\n"
  },
  {
    "path": "pkg/protoio/full.go",
    "content": "// Protocol Buffers for Go with Gadgets\n//\n// Copyright (c) 2013, The GoGo Authors. All rights reserved.\n// http://github.com/gogo/protobuf\n//\n// Redistribution and use in source and binary forms, with or without\n// modification, are permitted provided that the following conditions are\n// met:\n//\n//     * Redistributions of source code must retain the above copyright\n// notice, this list of conditions and the following disclaimer.\n//     * Redistributions in binary form must reproduce the above\n// copyright notice, this list of conditions and the following disclaimer\n// in the documentation and/or other materials provided with the\n// distribution.\n//\n// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n// \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\npackage protoio\n\nimport (\n\t\"io\"\n\n\t\"google.golang.org/protobuf/proto\"\n)\n\nfunc NewFullWriter(w io.Writer) WriteCloser {\n\treturn &fullWriter{w, nil}\n}\n\ntype fullWriter struct {\n\tw      io.Writer\n\tbuffer []byte\n}\n\nfunc (writer *fullWriter) WriteMsg(msg proto.Message) (err error) {\n\tvar data []byte\n\tif m, ok := msg.(marshaler); ok {\n\t\tn, ok := getSize(m)\n\t\tif !ok {\n\t\t\t_, err = proto.Marshal(msg)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif n >= len(writer.buffer) {\n\t\t\twriter.buffer = make([]byte, n)\n\t\t}\n\t\t_, err = m.MarshalTo(writer.buffer)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdata = writer.buffer[:n]\n\t} else {\n\t\tdata, err = proto.Marshal(msg)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\t_, err = writer.w.Write(data)\n\treturn err\n}\n\nfunc (writer *fullWriter) Close() error {\n\tif closer, ok := writer.w.(io.Closer); ok {\n\t\treturn closer.Close()\n\t}\n\treturn nil\n}\n\ntype fullReader struct {\n\tr   io.Reader\n\tbuf []byte\n}\n\nfunc NewFullReader(r io.Reader, maxSize int) ReadCloser {\n\treturn &fullReader{r, make([]byte, maxSize)}\n}\n\nfunc (reader *fullReader) ReadMsg(msg proto.Message) error {\n\tlength, err := reader.r.Read(reader.buf)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn proto.Unmarshal(reader.buf[:length], msg)\n}\n\nfunc (reader *fullReader) Close() error {\n\tif closer, ok := reader.r.(io.Closer); ok {\n\t\treturn closer.Close()\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/protoio/io.go",
    "content": "// Protocol Buffers for Go with Gadgets\n//\n// Copyright (c) 2013, The GoGo Authors. All rights reserved.\n// http://github.com/gogo/protobuf\n//\n// Redistribution and use in source and binary forms, with or without\n// modification, are permitted provided that the following conditions are\n// met:\n//\n//     * Redistributions of source code must retain the above copyright\n// notice, this list of conditions and the following disclaimer.\n//     * Redistributions in binary form must reproduce the above\n// copyright notice, this list of conditions and the following disclaimer\n// in the documentation and/or other materials provided with the\n// distribution.\n//\n// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n// \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\npackage protoio\n\nimport (\n\t\"io\"\n\n\t\"google.golang.org/protobuf/proto\"\n)\n\ntype Writer interface {\n\tWriteMsg(proto.Message) error\n}\n\ntype WriteCloser interface {\n\tWriter\n\tio.Closer\n}\n\ntype Reader interface {\n\tReadMsg(msg proto.Message) error\n}\n\ntype ReadCloser interface {\n\tReader\n\tio.Closer\n}\n\ntype marshaler interface {\n\tMarshalTo(data []byte) (n int, err error)\n}\n\nfunc getSize(v any) (int, bool) {\n\tif sz, ok := v.(interface {\n\t\tSize() (n int)\n\t}); ok {\n\t\treturn sz.Size(), true\n\t} else if sz, ok := v.(interface {\n\t\tProtoSize() (n int)\n\t}); ok {\n\t\treturn sz.ProtoSize(), true\n\t}\n\n\treturn 0, false\n}\n"
  },
  {
    "path": "pkg/protoio/uint32.go",
    "content": "// Protocol Buffers for Go with Gadgets\n//\n// Copyright (c) 2013, The GoGo Authors. All rights reserved.\n// http://github.com/gogo/protobuf\n//\n// Redistribution and use in source and binary forms, with or without\n// modification, are permitted provided that the following conditions are\n// met:\n//\n//     * Redistributions of source code must retain the above copyright\n// notice, this list of conditions and the following disclaimer.\n//     * Redistributions in binary form must reproduce the above\n// copyright notice, this list of conditions and the following disclaimer\n// in the documentation and/or other materials provided with the\n// distribution.\n//\n// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n// \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\npackage protoio\n\nimport (\n\t\"encoding/binary\"\n\t\"io\"\n\n\t\"google.golang.org/protobuf/proto\"\n)\n\nconst uint32BinaryLen = 4\n\nfunc NewUint32DelimitedWriter(w io.Writer, byteOrder binary.ByteOrder) WriteCloser {\n\treturn &uint32Writer{w, byteOrder, nil, make([]byte, uint32BinaryLen)}\n}\n\nfunc NewSizeUint32DelimitedWriter(w io.Writer, byteOrder binary.ByteOrder, size int) WriteCloser {\n\treturn &uint32Writer{w, byteOrder, make([]byte, size), make([]byte, uint32BinaryLen)}\n}\n\ntype uint32Writer struct {\n\tw         io.Writer\n\tbyteOrder binary.ByteOrder\n\tbuffer    []byte\n\tlenBuf    []byte\n}\n\nfunc (writer *uint32Writer) writeFallback(msg proto.Message) error {\n\tdata, err := proto.Marshal(msg)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tlength := uint32(len(data))\n\twriter.byteOrder.PutUint32(writer.lenBuf, length)\n\tif _, err = writer.w.Write(writer.lenBuf); err != nil {\n\t\treturn err\n\t}\n\t_, err = writer.w.Write(data)\n\treturn err\n}\n\nfunc (writer *uint32Writer) WriteMsg(msg proto.Message) error {\n\tm, ok := msg.(marshaler)\n\tif !ok {\n\t\treturn writer.writeFallback(msg)\n\t}\n\n\tn, ok := getSize(m)\n\tif !ok {\n\t\treturn writer.writeFallback(msg)\n\t}\n\n\tsize := n + uint32BinaryLen\n\tif size > len(writer.buffer) {\n\t\twriter.buffer = make([]byte, size)\n\t}\n\n\twriter.byteOrder.PutUint32(writer.buffer, uint32(n))\n\tif _, err := m.MarshalTo(writer.buffer[uint32BinaryLen:]); err != nil {\n\t\treturn err\n\t}\n\n\t_, err := writer.w.Write(writer.buffer[:size])\n\treturn err\n}\n\nfunc (writer *uint32Writer) Close() error {\n\tif closer, ok := writer.w.(io.Closer); ok {\n\t\treturn closer.Close()\n\t}\n\treturn nil\n}\n\ntype uint32Reader struct {\n\tr         io.Reader\n\tbyteOrder binary.ByteOrder\n\tlenBuf    []byte\n\tbuf       []byte\n\tmaxSize   int\n}\n\nfunc NewUint32DelimitedReader(r io.Reader, byteOrder binary.ByteOrder, maxSize int) ReadCloser {\n\treturn &uint32Reader{r, byteOrder, make([]byte, 4), nil, maxSize}\n}\n\nfunc (reader *uint32Reader) ReadMsg(msg proto.Message) error {\n\tif _, err := io.ReadFull(reader.r, reader.lenBuf); err != nil {\n\t\treturn err\n\t}\n\tlength32 := reader.byteOrder.Uint32(reader.lenBuf)\n\tlength := int(length32)\n\tif length < 0 || length > reader.maxSize {\n\t\treturn io.ErrShortBuffer\n\t}\n\tif length > len(reader.buf) {\n\t\treader.buf = make([]byte, length)\n\t}\n\t_, err := io.ReadFull(reader.r, reader.buf[:length])\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn proto.Unmarshal(reader.buf[:length], msg)\n}\n\nfunc (reader *uint32Reader) Close() error {\n\tif closer, ok := reader.r.(io.Closer); ok {\n\t\treturn closer.Close()\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/protoio/varint.go",
    "content": "// Protocol Buffers for Go with Gadgets\n//\n// Copyright (c) 2013, The GoGo Authors. All rights reserved.\n// http://github.com/gogo/protobuf\n//\n// Redistribution and use in source and binary forms, with or without\n// modification, are permitted provided that the following conditions are\n// met:\n//\n//     * Redistributions of source code must retain the above copyright\n// notice, this list of conditions and the following disclaimer.\n//     * Redistributions in binary form must reproduce the above\n// copyright notice, this list of conditions and the following disclaimer\n// in the documentation and/or other materials provided with the\n// distribution.\n//\n// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n// \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\npackage protoio\n\nimport (\n\t\"bufio\"\n\t\"encoding/binary\"\n\t\"io\"\n\n\t\"google.golang.org/protobuf/proto\"\n)\n\nfunc NewDelimitedWriter(w io.Writer) WriteCloser {\n\treturn &varintWriter{w, make([]byte, binary.MaxVarintLen64), nil}\n}\n\ntype varintWriter struct {\n\tw      io.Writer\n\tlenBuf []byte\n\tbuffer []byte\n}\n\nfunc (writer *varintWriter) WriteMsg(msg proto.Message) (err error) {\n\tvar data []byte\n\tif m, ok := msg.(marshaler); ok {\n\t\tn, ok := getSize(m)\n\t\tif ok {\n\t\t\tif n+binary.MaxVarintLen64 >= len(writer.buffer) {\n\t\t\t\twriter.buffer = make([]byte, n+binary.MaxVarintLen64)\n\t\t\t}\n\t\t\tlenOff := binary.PutUvarint(writer.buffer, uint64(n))\n\t\t\t_, err = m.MarshalTo(writer.buffer[lenOff:])\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\t_, err = writer.w.Write(writer.buffer[:lenOff+n])\n\t\t\treturn err\n\t\t}\n\t}\n\n\t// fallback\n\tdata, err = proto.Marshal(msg)\n\tif err != nil {\n\t\treturn err\n\t}\n\tlength := uint64(len(data))\n\tn := binary.PutUvarint(writer.lenBuf, length)\n\t_, err = writer.w.Write(writer.lenBuf[:n])\n\tif err != nil {\n\t\treturn err\n\t}\n\t_, err = writer.w.Write(data)\n\treturn err\n}\n\nfunc (writer *varintWriter) Close() error {\n\tif closer, ok := writer.w.(io.Closer); ok {\n\t\treturn closer.Close()\n\t}\n\treturn nil\n}\n\nfunc NewDelimitedReader(r io.Reader, maxSize int) ReadCloser {\n\tvar closer io.Closer\n\tif c, ok := r.(io.Closer); ok {\n\t\tcloser = c\n\t}\n\treturn &varintReader{bufio.NewReader(r), nil, maxSize, closer}\n}\n\ntype varintReader struct {\n\tr       *bufio.Reader\n\tbuf     []byte\n\tmaxSize int\n\tcloser  io.Closer\n}\n\nfunc (reader *varintReader) ReadMsg(msg proto.Message) error {\n\tlength64, err := binary.ReadUvarint(reader.r)\n\tif err != nil {\n\t\treturn err\n\t}\n\tlength := int(length64)\n\tif length < 0 || length > reader.maxSize {\n\t\treturn io.ErrShortBuffer\n\t}\n\tif len(reader.buf) < length {\n\t\treader.buf = make([]byte, length)\n\t}\n\tbuf := reader.buf[:length]\n\tif _, err := io.ReadFull(reader.r, buf); err != nil {\n\t\treturn err\n\t}\n\treturn proto.Unmarshal(buf, msg)\n}\n\nfunc (reader *varintReader) Close() error {\n\tif reader.closer != nil {\n\t\treturn reader.closer.Close()\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/proximitytransport/addr.go",
    "content": "package proximitytransport\n\nimport \"net\"\n\n// Addr is a net.Addr.\nvar _ net.Addr = &Addr{}\n\n// Addr represents a network end point address.\ntype Addr struct {\n\tAddress   string\n\ttransport *proximityTransport\n}\n\n// Network returns the address's network name.\nfunc (b *Addr) Network() string { return b.transport.driver.ProtocolName() }\n\n// String return's the string form of the address.\nfunc (b *Addr) String() string { return b.Address }\n"
  },
  {
    "path": "pkg/proximitytransport/conn.go",
    "content": "package proximitytransport\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/libp2p/go-libp2p/core/network\"\n\tpeer \"github.com/libp2p/go-libp2p/core/peer\"\n\ttpt \"github.com/libp2p/go-libp2p/core/transport\"\n\tma \"github.com/multiformats/go-multiaddr\"\n\tmanet \"github.com/multiformats/go-multiaddr/net\"\n\t\"github.com/pkg/errors\"\n\t\"go.uber.org/zap\"\n\n\t\"berty.tech/weshnet/v2/pkg/logutil\"\n)\n\n// Conn is a manet.Conn.\nvar _ manet.Conn = &Conn{}\n\n// Conn is the equivalent of a net.Conn object. It is the\n// result of calling the Dial or Listen functions in this\n// package, with associated local and remote Multiaddrs.\ntype Conn struct {\n\treadIn  *io.PipeWriter\n\treadOut *io.PipeReader\n\n\tlocalMa  ma.Multiaddr\n\tremoteMa ma.Multiaddr\n\n\tready bool\n\tsync.Mutex\n\tcache *RingBufferMap\n\tmp    *mplex\n\n\tctx       context.Context\n\tcancel    func()\n\ttransport *proximityTransport\n}\n\n// newConn returns an inbound or outbound tpt.CapableConn upgraded from a Conn.\nfunc newConn(ctx context.Context, t *proximityTransport, remoteMa ma.Multiaddr, remotePID peer.ID, netdir network.Direction,\n) (tpt.CapableConn, error) {\n\tt.logger.Debug(\"newConn()\", logutil.PrivateString(\"remoteMa\", remoteMa.String()), zap.Bool(\"inbound\", netdir == network.DirInbound))\n\n\tif netdir == network.DirUnknown {\n\t\treturn nil, fmt.Errorf(\"invalid network direction\")\n\t}\n\n\tconnScope, err := t.swarm.ResourceManager().OpenConnection(netdir, false, remoteMa)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"resource manager blocked connection : %w\", err)\n\t}\n\n\t// Creates a manet.Conn\n\tpr, pw := io.Pipe()\n\tconnCtx, cancel := context.WithCancel(t.listener.ctx)\n\n\tmaconn := &Conn{\n\t\treadIn:    pw,\n\t\treadOut:   pr,\n\t\tlocalMa:   t.listener.localMa,\n\t\tremoteMa:  remoteMa,\n\t\tready:     false,\n\t\tcache:     NewRingBufferMap(t.logger, 128),\n\t\tmp:        newMplex(connCtx, t.logger),\n\t\tctx:       connCtx,\n\t\tcancel:    cancel,\n\t\ttransport: t,\n\t}\n\n\t// Stores the conn in connMap, will be deleted during conn.Close()\n\tt.connMapMutex.Lock()\n\tt.connMap[maconn.RemoteAddr().String()] = maconn\n\tt.connMapMutex.Unlock()\n\n\t// Configure mplex and run it\n\tmaconn.mp.addInputCache(t.cache)\n\tmaconn.mp.addInputCache(maconn.cache)\n\tmaconn.mp.setOutput(pw)\n\n\t// Returns an upgraded CapableConn (muxed, addr filtered, secured, etc...)\n\treturn t.upgrader.Upgrade(ctx, t, maconn, netdir, remotePID, connScope)\n}\n\n// Read reads data from the connection.\n// Timeout handled by the native driver.\nfunc (c *Conn) Read(payload []byte) (n int, err error) {\n\tc.transport.logger.Debug(\"Conn.Read\", logutil.PrivateString(\"remoteAddr\", c.RemoteAddr().String()))\n\tif c.ctx.Err() != nil {\n\t\tc.transport.logger.Error(\"Conn.Read failed: conn already closed\")\n\t\treturn 0, fmt.Errorf(\"error: Conn.Read failed: conn already closed\")\n\t}\n\n\tn, err = c.readOut.Read(payload)\n\tif err != nil {\n\t\terr = errors.Wrap(err, \"error: Conn.Read failed: native read failed\")\n\t\tc.transport.logger.Error(\"Conn.Read\", zap.Error(err))\n\t} else {\n\t\tc.transport.logger.Debug(\"Conn.Read successful\")\n\t}\n\treturn n, err\n}\n\n// Write writes data to the connection.\n// Timeout handled by the native driver.\nfunc (c *Conn) Write(payload []byte) (n int, err error) {\n\tc.transport.logger.Debug(\"Conn.Write\", logutil.PrivateString(\"remoteAddr\", c.RemoteAddr().String()), logutil.PrivateBinary(\"payload\", payload))\n\tif c.ctx.Err() != nil {\n\t\treturn 0, fmt.Errorf(\"error: Conn.Write failed: conn already closed\")\n\t}\n\n\t// Set connection as ready and flush cached payloads\n\tif !c.isReady() {\n\t\tc.Lock()\n\t\tif !c.ready {\n\t\t\tc.ready = true\n\t\t}\n\t\tc.Unlock()\n\t\tgo c.mp.run(c.RemoteAddr().String())\n\t}\n\n\t// Write to the peer's device using native driver.\n\tif !c.transport.driver.SendToPeer(c.RemoteAddr().String(), payload) {\n\t\tc.transport.logger.Error(\"Conn.Write failed\")\n\t\treturn 0, fmt.Errorf(\"error: Conn.Write failed: native write failed\")\n\t}\n\tc.transport.logger.Debug(\"Conn.Write successful\")\n\n\treturn len(payload), nil\n}\n\n// Close closes the connection.\n// Any blocked Read or Write operations will be unblocked and return errors.\nfunc (c *Conn) Close() error {\n\tc.transport.logger.Debug(\"Conn.Close()\")\n\tc.cancel()\n\n\t// Closes read pipe\n\tc.readIn.Close()\n\tc.readOut.Close()\n\n\t// Removes conn from connmgr's connMap\n\tc.transport.connMapMutex.Lock()\n\tdelete(c.transport.connMap, c.RemoteAddr().String())\n\tc.transport.connMapMutex.Unlock()\n\n\t// Disconnect the driver\n\tc.transport.driver.CloseConnWithPeer(c.RemoteAddr().String())\n\n\treturn nil\n}\n\n// isReady tells if  libp2p is ready to accept input connections\nfunc (c *Conn) isReady() bool {\n\tc.Lock()\n\tdefer c.Unlock()\n\treturn c.ready\n}\n\n// LocalAddr returns the local network address.\nfunc (c *Conn) LocalAddr() net.Addr {\n\tlAddr, _ := c.LocalMultiaddr().ValueForProtocol(c.transport.driver.ProtocolCode())\n\treturn &Addr{\n\t\tAddress:   lAddr,\n\t\ttransport: c.transport,\n\t}\n}\n\n// RemoteAddr returns the remote network address.\nfunc (c *Conn) RemoteAddr() net.Addr {\n\trAddr, _ := c.RemoteMultiaddr().ValueForProtocol(c.transport.driver.ProtocolCode())\n\treturn &Addr{\n\t\tAddress:   rAddr,\n\t\ttransport: c.transport,\n\t}\n}\n\n// LocalMultiaddr returns the local Multiaddr associated\n// with this connection.\nfunc (c *Conn) LocalMultiaddr() ma.Multiaddr { return c.localMa }\n\n// RemoteMultiaddr returns the remote Multiaddr associated\n// with this connection.\nfunc (c *Conn) RemoteMultiaddr() ma.Multiaddr { return c.remoteMa }\n\n// Noop deadline methods, handled by the native driver.\n\n// SetDeadline does nothing\nfunc (c *Conn) SetDeadline(time.Time) error { return nil }\n\n// SetReadDeadline does nothing\nfunc (c *Conn) SetReadDeadline(time.Time) error { return nil }\n\n// SetWriteDeadline does nothing\nfunc (c *Conn) SetWriteDeadline(time.Time) error { return nil }\n"
  },
  {
    "path": "pkg/proximitytransport/example_test.go",
    "content": "package proximitytransport_test\n"
  },
  {
    "path": "pkg/proximitytransport/listener.go",
    "content": "package proximitytransport\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"net\"\n\n\tnetwork \"github.com/libp2p/go-libp2p/core/network\"\n\tpeer \"github.com/libp2p/go-libp2p/core/peer\"\n\ttpt \"github.com/libp2p/go-libp2p/core/transport\"\n\tma \"github.com/multiformats/go-multiaddr\"\n)\n\n// Listener is a tpt.Listener.\nvar _ tpt.Listener = &Listener{}\n\n// Listener is an interface closely resembling the net.Listener interface. The\n// only real difference is that Accept() returns Conn's of the type in this\n// package, and also exposes a Multiaddr method as opposed to a regular Addr\n// method.\ntype Listener struct {\n\ttransport      *proximityTransport\n\tlocalMa        ma.Multiaddr\n\tinboundConnReq chan connReq // Chan used to accept inbound conn.\n\tctx            context.Context\n\tcancel         func()\n}\n\n// connReq holds data necessary for inbound conn creation.\ntype connReq struct {\n\tremoteMa  ma.Multiaddr\n\tremotePID peer.ID\n}\n\n// newListener starts the native driver then returns a new Listener.\nfunc newListener(ctx context.Context, localMa ma.Multiaddr, t *proximityTransport) *Listener {\n\tt.logger.Debug(\"newListener()\")\n\tctx, cancel := context.WithCancel(ctx)\n\n\tlistener := &Listener{\n\t\ttransport:      t,\n\t\tlocalMa:        localMa,\n\t\tinboundConnReq: make(chan connReq),\n\t\tctx:            ctx,\n\t\tcancel:         cancel,\n\t}\n\n\t// Starts the native driver.\n\t// If it failed, don't return a error because no other transport\n\t// on the libp2p node will be created.\n\tt.driver.Start(t.swarm.LocalPeer().String())\n\n\treturn listener\n}\n\n// Accept waits for and returns the next connection to the listener.\n// Returns a Multiaddr friendly Conn.\nfunc (l *Listener) Accept() (tpt.CapableConn, error) {\n\tfor {\n\t\tselect {\n\t\tcase req := <-l.inboundConnReq:\n\t\t\tl.transport.logger.Debug(\"Listener.Accept(): incoming connection\")\n\t\t\tconn, err := newConn(l.ctx, l.transport, req.remoteMa, req.remotePID, network.DirInbound)\n\t\t\t// If the newConn failed for some reason, Accept won't return an error\n\t\t\t// because otherwise it will close the listener\n\t\t\tif err == nil {\n\t\t\t\treturn conn, nil\n\t\t\t}\n\t\tcase <-l.ctx.Done():\n\t\t\treturn nil, errors.New(\"error: Listener.Accept failed: listener already closed\")\n\t\t}\n\t}\n}\n\n// Close closes the listener.\n// Any blocked Accept operations will be unblocked and return errors.\nfunc (l *Listener) Close() error {\n\tl.transport.logger.Debug(\"Listener.Close()\")\n\tl.cancel()\n\n\t// Stops the native driver.\n\tl.transport.driver.Stop()\n\n\t// Removes listener so transport can instantiate a new one later.\n\tl.transport.lock.Lock()\n\tl.transport.listener = nil\n\tl.transport.lock.Unlock()\n\n\t// Unregister this transport\n\tTransportMapMutex.Lock()\n\tdelete(TransportMap, l.transport.driver.ProtocolName())\n\tTransportMapMutex.Unlock()\n\n\treturn nil\n}\n\n// Multiaddr returns the listener's (local) Multiaddr.\nfunc (l *Listener) Multiaddr() ma.Multiaddr { return l.localMa }\n\n// Addr returns the net.Listener's network address.\nfunc (l *Listener) Addr() net.Addr {\n\tlAddr, _ := l.localMa.ValueForProtocol(l.transport.driver.ProtocolCode())\n\treturn &Addr{\n\t\tAddress: lAddr,\n\t}\n}\n"
  },
  {
    "path": "pkg/proximitytransport/mplex.go",
    "content": "package proximitytransport\n\n/*\n  The mplex struct as NOT relationship with libp2p mplex package!!!\n\n  This mplex struct is a multiplexer taken multiple inputs for one output.\n  You must initialize mplex by setting input and output before running it.\n  There are two types of input:\n  1) RingBufferMap\n  2) builtin chan []byte\n  There is only one type of output: *io.PipeWriter\n  When you start mplex, its flushed buffers first in the order you set them,\n  and read on its chan []byte.\n*/\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"sync\"\n\n\t\"go.uber.org/zap\"\n\n\t\"berty.tech/weshnet/v2/pkg/logutil\"\n)\n\ntype mplex struct {\n\tinputCaches []*RingBufferMap\n\tinputLock   sync.Mutex\n\tinput       chan []byte\n\n\toutput *io.PipeWriter\n\n\tctx    context.Context\n\tlogger *zap.Logger\n}\n\nfunc newMplex(ctx context.Context, logger *zap.Logger) *mplex {\n\tlogger = logger.Named(\"mplex\")\n\treturn &mplex{\n\t\tinput:  make(chan []byte),\n\t\tctx:    ctx,\n\t\tlogger: logger,\n\t}\n}\n\nfunc (m *mplex) setOutput(o *io.PipeWriter) {\n\tm.output = o\n}\n\nfunc (m *mplex) addInputCache(c *RingBufferMap) {\n\tm.inputLock.Lock()\n\tm.inputCaches = append(m.inputCaches, c)\n\tm.inputLock.Unlock()\n}\n\nfunc (m *mplex) write(s []byte) {\n\tm.logger.Debug(\"write\", logutil.PrivateBinary(\"payload\", s))\n\t_, err := m.output.Write(s)\n\tif err != nil {\n\t\tm.logger.Error(\"write: write pipe error\", zap.Error(err))\n\t} else {\n\t\tm.logger.Debug(\"write: successful write pipe\")\n\t}\n}\n\n// run flushes caches and read input channel\nfunc (m *mplex) run(peerID string) {\n\tm.logger.Debug(\"run: started\")\n\t// flush caches\n\tm.inputLock.Lock()\n\tfor _, cache := range m.inputCaches {\n\t\tm.logger.Debug(\"run: flushing one cache\")\n\n\t\tpayloads := cache.Flush(peerID)\n\t\tfor payload := range payloads {\n\t\t\tm.write(payload)\n\t\t}\n\t}\n\tm.inputLock.Unlock()\n\n\t// read input\n\tm.logger.Debug(\"run: reading input channel\")\n\tfor {\n\t\tselect {\n\t\tcase payload := <-m.input:\n\t\t\tm.write(payload)\n\t\tcase <-m.ctx.Done():\n\t\t\treturn\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "pkg/proximitytransport/proximitydriver.go",
    "content": "package proximitytransport\n\ntype ProximityDriver interface {\n\t// Start the native driver\n\tStart(localPID string)\n\n\t// Stop the native driver\n\tStop()\n\n\t// Check if the native driver is connected to the remote peer\n\tDialPeer(remotePID string) bool\n\n\t// Send data to the remote peer\n\tSendToPeer(remotePID string, payload []byte) bool\n\n\t// Close the connection with the remote peer\n\tCloseConnWithPeer(remotePID string)\n\n\t// Return the multiaddress protocol code\n\tProtocolCode() int\n\n\t// Return the multiaddress protocol name\n\tProtocolName() string\n\n\t// Return the default multiaddress\n\tDefaultAddr() string\n}\n\ntype NoopProximityDriver struct {\n\tprotocolCode int\n\tprotocolName string\n\tdefaultAddr  string\n}\n\nfunc NewNoopProximityDriver(protocolCode int, protocolName, defaultAddr string) *NoopProximityDriver {\n\treturn &NoopProximityDriver{\n\t\tprotocolCode: protocolCode,\n\t\tprotocolName: protocolName,\n\t\tdefaultAddr:  defaultAddr,\n\t}\n}\n\nfunc (d *NoopProximityDriver) Start(_ string) {}\n\nfunc (d *NoopProximityDriver) Stop() {}\n\nfunc (d *NoopProximityDriver) DialPeer(_ string) bool { return false }\n\nfunc (d *NoopProximityDriver) SendToPeer(_ string, _ []byte) bool { return false }\n\nfunc (d *NoopProximityDriver) CloseConnWithPeer(_ string) {}\n\nfunc (d *NoopProximityDriver) ProtocolCode() int { return d.protocolCode }\n\nfunc (d *NoopProximityDriver) ProtocolName() string { return d.protocolName }\n\nfunc (d *NoopProximityDriver) DefaultAddr() string { return d.defaultAddr }\n"
  },
  {
    "path": "pkg/proximitytransport/ringBuffer_map.go",
    "content": "package proximitytransport\n\nimport (\n\t\"container/ring\"\n\t\"sync\"\n\n\t\"go.uber.org/zap\"\n\n\t\"berty.tech/weshnet/v2/pkg/logutil\"\n)\n\n// RingBufferMap is a map of string:ringBuffer(aka circular buffer)\n// The key is a peerID.\ntype RingBufferMap struct {\n\tsync.Mutex\n\n\tcache      map[string]*ringBuffer\n\tbufferSize int\n\tlogger     *zap.Logger\n}\n\ntype ringBuffer struct {\n\tsync.Mutex\n\tbuffer *ring.Ring\n}\n\n// NewRingBufferMap returns a new connMgr struct\n// The size argument is the number of packets to save in cache.\nfunc NewRingBufferMap(logger *zap.Logger, size int) *RingBufferMap {\n\tlogger = logger.Named(\"RingBuffer\")\n\treturn &RingBufferMap{\n\t\tcache:      make(map[string]*ringBuffer),\n\t\tbufferSize: size,\n\t\tlogger:     logger,\n\t}\n}\n\n// Add adds the payload into a circular cache\nfunc (rbm *RingBufferMap) Add(peerID string, payload []byte) {\n\trbm.logger.Debug(\"Add\", logutil.PrivateString(\"peerID\", peerID), logutil.PrivateBinary(\"payload\", payload))\n\n\tvar rBuffer *ringBuffer\n\n\trbm.Lock()\n\trBuffer, ok := rbm.cache[peerID]\n\trbm.Unlock()\n\tif !ok {\n\t\trBuffer = &ringBuffer{\n\t\t\tbuffer: ring.New(rbm.bufferSize),\n\t\t}\n\t}\n\n\trBuffer.Lock()\n\trBuffer.buffer.Value = payload\n\trBuffer.buffer = rBuffer.buffer.Next()\n\trBuffer.Unlock()\n\n\trbm.Lock()\n\trbm.cache[peerID] = rBuffer\n\trbm.Unlock()\n}\n\n// Flush puts the cache contents into a chan and clears it\nfunc (rbm *RingBufferMap) Flush(peerID string) <-chan []byte {\n\trbm.logger.Debug(\"flushCache\", logutil.PrivateString(\"peerID\", peerID))\n\n\tc := make(chan []byte)\n\n\tgo func() {\n\t\trbm.Lock()\n\t\trBuffer, ok := rbm.cache[peerID]\n\t\trbm.Unlock()\n\n\t\tif ok {\n\t\t\trBuffer.Lock()\n\t\t\tfor i := 0; i < rbm.bufferSize; i++ {\n\t\t\t\tpayload, ok := rBuffer.buffer.Value.([]byte)\n\t\t\t\tif !ok {\n\t\t\t\t\trBuffer.buffer = rBuffer.buffer.Next()\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\trbm.logger.Debug(\"flushCache\", logutil.PrivateBinary(\"payload\", payload))\n\t\t\t\tc <- payload\n\n\t\t\t\trBuffer.buffer.Value = nil\n\t\t\t\trBuffer.buffer = rBuffer.buffer.Next()\n\t\t\t}\n\t\t\trBuffer.Unlock()\n\n\t\t\trbm.Lock()\n\t\t\tdelete(rbm.cache, peerID)\n\t\t\trbm.Unlock()\n\t\t}\n\n\t\tclose(c)\n\t}()\n\n\treturn c\n}\n\n// Delete cache entry\nfunc (rbm *RingBufferMap) Delete(peerID string) {\n\trbm.logger.Debug(\"RingBufferMap: Delete called\", logutil.PrivateString(\"peerID\", peerID))\n\n\trbm.Lock()\n\t_, ok := rbm.cache[peerID]\n\tif ok {\n\t\trbm.logger.Debug(\"RingBufferMap: Delete: cache found\", logutil.PrivateString(\"peerID\", peerID))\n\n\t\tdelete(rbm.cache, peerID)\n\t}\n\trbm.Unlock()\n}\n"
  },
  {
    "path": "pkg/proximitytransport/transport.go",
    "content": "package proximitytransport\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"sync\"\n\n\tnetwork \"github.com/libp2p/go-libp2p/core/network\"\n\tpeer \"github.com/libp2p/go-libp2p/core/peer\"\n\tpstore \"github.com/libp2p/go-libp2p/core/peerstore\"\n\ttpt \"github.com/libp2p/go-libp2p/core/transport\"\n\t\"github.com/libp2p/go-libp2p/p2p/net/swarm\"\n\tma \"github.com/multiformats/go-multiaddr\"\n\tmafmt \"github.com/multiformats/go-multiaddr-fmt\"\n\t\"github.com/pkg/errors\"\n\t\"go.uber.org/zap\"\n\n\t\"berty.tech/weshnet/v2/pkg/logutil\"\n)\n\n// The ProximityTransport is a libp2p transport that initializes NativeDriver.\n// It allows connecting to nearby peers\n\n// proximityTransport is a tpt.transport.\nvar _ tpt.Transport = &proximityTransport{}\n\n// proximityTransport is a ProximityTransport.\nvar _ ProximityTransport = &proximityTransport{}\n\n// TransportMap prevents instantiating multiple Transport\nvar TransportMap = make(map[string]*proximityTransport)\n\n// TransportMapMutex is the mutex for the TransportMap var\nvar TransportMapMutex sync.RWMutex\n\n// Define log level for driver loggers\nconst (\n\tVerbose = iota\n\tDebug\n\tInfo\n\tWarn\n\tError\n)\n\ntype ProximityTransport interface {\n\tHandleFoundPeer(remotePID string) bool\n\tHandleLostPeer(remotePID string)\n\tReceiveFromPeer(remotePID string, payload []byte)\n\tLog(level int, message string)\n}\n\ntype proximityTransport struct {\n\tswarm    *swarm.Swarm\n\tupgrader tpt.Upgrader\n\n\tconnMap      map[string]*Conn\n\tconnMapMutex sync.RWMutex\n\tcache        *RingBufferMap\n\tlock         sync.RWMutex\n\tlistener     *Listener\n\tdriver       ProximityDriver\n\tlogger       *zap.Logger\n\tctx          context.Context\n}\n\nfunc NewTransport(ctx context.Context, l *zap.Logger, driver ProximityDriver) func(swarm *swarm.Swarm, u tpt.Upgrader) (*proximityTransport, error) {\n\tif l == nil {\n\t\tl = zap.NewNop()\n\t}\n\n\tl = l.Named(\"ProximityTransport\")\n\n\tif driver == nil {\n\t\tl.Error(\"error: NewTransport: driver is nil\")\n\t\tdriver = &NoopProximityDriver{}\n\t}\n\n\tl.Debug(\"remi: transport.go: new Transport\")\n\treturn func(swarm *swarm.Swarm, u tpt.Upgrader) (*proximityTransport, error) {\n\t\tl.Debug(\"NewTransport called\", zap.String(\"driver\", driver.ProtocolName()))\n\t\ttransport := &proximityTransport{\n\t\t\tswarm:    swarm,\n\t\t\tupgrader: u,\n\t\t\tconnMap:  make(map[string]*Conn),\n\t\t\tcache:    NewRingBufferMap(l, 128),\n\t\t\tdriver:   driver,\n\t\t\tlogger:   l,\n\t\t\tctx:      ctx,\n\t\t}\n\n\t\treturn transport, nil\n\t}\n}\n\n// Dial dials the peer at the remote address.\n// With proximity connections (e.g. MC, BLE, Nearby) you can only dial a device that is already connected with the native driver.\nfunc (t *proximityTransport) Dial(ctx context.Context, remoteMa ma.Multiaddr, remotePID peer.ID) (tpt.CapableConn, error) {\n\t// proximityTransport needs to have a running listener in order to dial other peer\n\t// because native driver is initialized during listener creation.\n\tt.lock.RLock()\n\tdefer t.lock.RUnlock()\n\tif t.listener == nil {\n\t\treturn nil, errors.New(\"error: proximityTransport.Dial: no active listener\")\n\t}\n\n\t// remoteAddr is supposed to be equal to remotePID since with proximity transports:\n\t// multiaddr = /<protocol>/<peerID>\n\tremoteAddr, err := remoteMa.ValueForProtocol(t.driver.ProtocolCode())\n\tif err != nil || remoteAddr != remotePID.String() {\n\t\treturn nil, errors.Wrap(err, \"error: proximityTransport.Dial: wrong multiaddr\")\n\t}\n\n\t// Check if native driver is already connected to peer's device.\n\t// With proximity connections you can't really dial, only auto-connect with peer nearby.\n\tif !t.driver.DialPeer(remoteAddr) {\n\t\treturn nil, errors.New(\"error: proximityTransport.Dial: peer not connected through the native driver\")\n\t}\n\n\t// Can't have two connections on the same multiaddr\n\tt.connMapMutex.RLock()\n\t_, ok := t.connMap[remoteAddr]\n\tt.connMapMutex.RUnlock()\n\tif ok {\n\t\treturn nil, errors.New(\"error: proximityTransport.Dial: already connected to this address\")\n\t}\n\n\t// Returns an outbound conn.\n\treturn newConn(ctx, t, remoteMa, remotePID, network.DirOutbound)\n}\n\n// CanDial returns true if this transport believes it can dial the given\n// multiaddr.\nfunc (t *proximityTransport) CanDial(remoteMa ma.Multiaddr) bool {\n\t// multiaddr validation checker\n\treturn mafmt.Base(t.driver.ProtocolCode()).Matches(remoteMa)\n}\n\n// Listen listens on the given multiaddr.\n// Proximity connections can't listen on more than one listener.\nfunc (t *proximityTransport) Listen(localMa ma.Multiaddr) (tpt.Listener, error) {\n\t// localAddr is supposed to be equal to the localPID\n\t// or to DefaultAddr since multiaddr == /<protocol>/<peerID>\n\tlocalPID := t.swarm.LocalPeer().String()\n\tlocalAddr, err := localMa.ValueForProtocol(t.driver.ProtocolCode())\n\tif err != nil || (localMa.String() != t.driver.DefaultAddr() && localAddr != localPID) {\n\t\treturn nil, errors.Wrap(err, \"error: proximityTransport.Listen: wrong multiaddr\")\n\t}\n\n\t// Replaces default bind by local host peerID\n\tif localMa.String() == t.driver.DefaultAddr() {\n\t\tlocalMa, err = ma.NewMultiaddr(fmt.Sprintf(\"/%s/%s\", t.driver.ProtocolName(), localPID))\n\t\tif err != nil { // Should never append.\n\t\t\tpanic(err)\n\t\t}\n\t}\n\n\t// If the a listener already exists for this driver, returns an error.\n\tTransportMapMutex.RLock()\n\t_, ok := TransportMap[t.driver.ProtocolName()]\n\tTransportMapMutex.RUnlock()\n\tt.lock.RLock()\n\tif ok || t.listener != nil {\n\t\tt.lock.RUnlock()\n\t\treturn nil, errors.New(\"error: proximityTransport.Listen: one listener maximum\")\n\t}\n\tt.lock.RUnlock()\n\n\t// Register this transport\n\tTransportMapMutex.Lock()\n\tTransportMap[t.driver.ProtocolName()] = t\n\tTransportMapMutex.Unlock()\n\n\tt.lock.Lock()\n\tdefer t.lock.Unlock()\n\n\tt.listener = newListener(t.ctx, localMa, t)\n\n\treturn t.listener, err\n}\n\n// ReceiveFromPeer is called by native driver when peer's device sent data.\n// If the connection is not found, data is added in the transport cache level.\n// If the connection is not activated yet, data is added in the connection cache level.\n// Cache are circular buffer, avoiding RAM memory attack.\nfunc (t *proximityTransport) ReceiveFromPeer(remotePID string, payload []byte) {\n\tt.logger.Debug(\"ReceiveFromPeer()\", zap.String(\"remotePID\", remotePID), logutil.PrivateBinary(\"payload\", payload))\n\n\t// copy value from driver\n\tdata := make([]byte, len(payload))\n\tcopy(data, payload)\n\n\tt.connMapMutex.RLock()\n\tc, ok := t.connMap[remotePID]\n\tt.connMapMutex.RUnlock()\n\tif ok {\n\t\t// Put payload in the Conn cache if libp2p connection is not ready\n\t\tif !c.isReady() {\n\t\t\tc.Lock()\n\t\t\tif !c.ready {\n\t\t\t\tt.logger.Info(\"ReceiveFromPeer: connection is not ready to accept incoming packets, add it to cache\")\n\t\t\t\tc.cache.Add(remotePID, data)\n\t\t\t\tc.Unlock()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tc.Unlock()\n\t\t}\n\n\t\t// Write the payload into pipe\n\t\tc.mp.input <- data\n\t} else {\n\t\tt.logger.Info(\"ReceiveFromPeer: no Conn found, put payload in cache\")\n\t\tt.cache.Add(remotePID, data)\n\t}\n}\n\n// HandleFoundPeer is called by the native driver when a new peer is found.\n// Adds the peer in the PeerStore and initiates a connection with it\nfunc (t *proximityTransport) HandleFoundPeer(sRemotePID string) bool {\n\tt.logger.Debug(\"HandleFoundPeer\", zap.String(\"remotePID\", sRemotePID))\n\tremotePID, err := peer.Decode(sRemotePID)\n\tif err != nil {\n\t\tt.logger.Error(\"HandleFoundPeer: wrong remote peerID\")\n\t\treturn false\n\t}\n\n\tremoteMa, err := ma.NewMultiaddr(fmt.Sprintf(\"/%s/%s\", t.driver.ProtocolName(), sRemotePID))\n\tif err != nil {\n\t\t// Should never occur\n\t\tpanic(err)\n\t}\n\n\t// Checks if a listener is currently running.\n\tt.lock.RLock()\n\n\tif t.listener == nil || t.listener.ctx.Err() != nil {\n\t\tt.lock.RUnlock()\n\t\tt.logger.Error(\"HandleFoundPeer: listener not running\")\n\t\treturn false\n\t}\n\n\t// Get snapshot of listener\n\tlistener := t.listener\n\n\t// unblock here to prevent blocking other APIs of Listener or Transport\n\tt.lock.RUnlock()\n\n\t// Adds peer to peerstore.\n\tt.swarm.Peerstore().AddAddr(remotePID, remoteMa,\n\t\tpstore.TempAddrTTL)\n\n\t// Delete previous cache if it exists\n\tt.cache.Delete(sRemotePID)\n\n\t// Peer with lexicographical smallest peerID inits libp2p connection.\n\tif listener.Addr().String() < sRemotePID {\n\t\tt.logger.Debug(\"HandleFoundPeer: outgoing libp2p connection\")\n\t\t// Async connect so HandleFoundPeer can return and unlock the native driver.\n\t\t// Needed to read and write during the connect handshake.\n\t\tgo func() {\n\t\t\t// Need to use listener than t.listener here to not have to check valid value of t.listener\n\t\t\terr := t.connect(listener.ctx, peer.AddrInfo{\n\t\t\t\tID:    remotePID,\n\t\t\t\tAddrs: []ma.Multiaddr{remoteMa},\n\t\t\t})\n\t\t\tif err != nil {\n\t\t\t\tt.logger.Error(\"HandleFoundPeer: async connect error\", zap.Error(err))\n\t\t\t\tt.swarm.Peerstore().SetAddr(remotePID, remoteMa, -1)\n\t\t\t\tt.driver.CloseConnWithPeer(sRemotePID)\n\t\t\t}\n\t\t}()\n\n\t\treturn true\n\t}\n\n\tt.logger.Debug(\"HandleFoundPeer: incoming libp2p connection\")\n\t// Peer with lexicographical biggest peerID accepts incoming connection.\n\t// FIXME : consider to push this code in go routine to prevent blocking native driver\n\tselect {\n\tcase listener.inboundConnReq <- connReq{\n\t\tremoteMa:  remoteMa,\n\t\tremotePID: remotePID,\n\t}:\n\t\treturn true\n\tcase <-listener.ctx.Done():\n\t\treturn false\n\t}\n}\n\n// Adapted from https://github.com/libp2p/go-libp2p/blob/v0.38.1/p2p/host/basic/basic_host.go#L795\nfunc (t *proximityTransport) connect(ctx context.Context, pi peer.AddrInfo) error {\n\t// absorb addresses into peerstore\n\tt.swarm.Peerstore().AddAddrs(pi.ID, pi.Addrs, pstore.TempAddrTTL)\n\n\tforceDirect, _ := network.GetForceDirectDial(ctx)\n\tcanUseLimitedConn, _ := network.GetAllowLimitedConn(ctx)\n\tif !forceDirect {\n\t\tconnectedness := t.swarm.Connectedness(pi.ID)\n\t\tif connectedness == network.Connected || (canUseLimitedConn && connectedness == network.Limited) {\n\t\t\treturn nil\n\t\t}\n\t}\n\n\t_, err := t.swarm.DialPeer(ctx, pi.ID)\n\treturn err\n}\n\n// HandleLostPeer is called by the native driver when the connection with the peer is lost.\n// Closes connections with the peer.\nfunc (t *proximityTransport) HandleLostPeer(sRemotePID string) {\n\tt.logger.Debug(\"HandleLostPeer\", logutil.PrivateString(\"remotePID\", sRemotePID))\n\tremotePID, err := peer.Decode(sRemotePID)\n\tif err != nil {\n\t\tt.logger.Error(\"HandleLostPeer: wrong remote peerID\")\n\t\treturn\n\t}\n\n\tremoteMa, err := ma.NewMultiaddr(fmt.Sprintf(\"/%s/%s\", t.driver.ProtocolName(), sRemotePID))\n\tif err != nil {\n\t\t// Should never occur\n\t\tpanic(err)\n\t}\n\n\t// Remove peer's address to peerstore.\n\tt.swarm.Peerstore().SetAddr(remotePID, remoteMa, -1)\n\n\t// Close the peer connection\n\tconns := t.swarm.ConnsToPeer(remotePID)\n\tfor _, conn := range conns {\n\t\tif conn.RemoteMultiaddr().Equal(remoteMa) {\n\t\t\tconn.Close()\n\t\t}\n\t}\n}\n\nfunc (t *proximityTransport) Log(level int, message string) {\n\tswitch level {\n\tcase Verbose, Debug:\n\t\tt.logger.Debug(message)\n\tcase Info:\n\t\tt.logger.Info(message)\n\tcase Warn:\n\t\tt.logger.Warn(message)\n\tcase Error:\n\t\tt.logger.Error(message)\n\t}\n}\n\n// Proxy returns true if this transport proxies.\nfunc (t *proximityTransport) Proxy() bool {\n\treturn false\n}\n\n// Protocols returns the set of protocols handled by this transport.\nfunc (t *proximityTransport) Protocols() []int {\n\treturn []int{t.driver.ProtocolCode()}\n}\n\nfunc (t *proximityTransport) String() string {\n\treturn t.driver.ProtocolName()\n}\n"
  },
  {
    "path": "pkg/rendezvous/emitterio_sync_client.go",
    "content": "package rendezvous\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"sync\"\n\t\"time\"\n\n\temitter \"github.com/berty/emitter-go/v2\"\n\trendezvous \"github.com/berty/go-libp2p-rendezvous\"\n\tpb \"github.com/berty/go-libp2p-rendezvous/pb\"\n\t\"github.com/libp2p/go-libp2p/core/peer\"\n\t\"github.com/multiformats/go-multiaddr\"\n\t\"go.uber.org/zap\"\n\t\"google.golang.org/protobuf/proto\"\n)\n\ntype SyncClient interface {\n\trendezvous.RendezvousSyncClient\n\n\tio.Closer\n}\n\ntype registrationMessage struct {\n\tregistration *rendezvous.Registration\n\tpsDetails    *EmitterPubSubSubscriptionDetails\n}\n\ntype emitterClient struct {\n\tlogger *zap.Logger\n\tclient *emitter.Client\n\tctx    context.Context\n\tcancel context.CancelFunc\n\tusage  int\n}\n\ntype emitterClientManager struct {\n\tctx      context.Context\n\tcancel   context.CancelFunc\n\tclients  map[string]*emitterClient\n\toutChans map[string][]chan *rendezvous.Registration\n\tinChan   chan *registrationMessage\n\tmu       sync.Mutex\n\tlogger   *zap.Logger\n}\n\nfunc (e *emitterClientManager) Close() (err error) {\n\te.mu.Lock()\n\tfor _, client := range e.clients {\n\t\tclient.Close()\n\t}\n\te.cancel()\n\te.mu.Unlock()\n\treturn nil\n}\n\nfunc (e *emitterClient) Close() (err error) {\n\te.cancel()\n\te.client.Disconnect(time.Millisecond * 100)\n\treturn nil\n}\n\nfunc (e *emitterClient) subscribeToServerUpdates(inChan chan *registrationMessage, psDetails *EmitterPubSubSubscriptionDetails) (err error) {\n\te.logger.Debug(\"subscribing\", zap.String(\"chan\", psDetails.ChannelName))\n\treturn e.client.Subscribe(psDetails.ReadKey, psDetails.ChannelName, func(_ *emitter.Client, message emitter.Message) {\n\t\treg := &pb.RegistrationRecord{}\n\n\t\te.logger.Debug(\"receiving a message\", zap.Any(\"topic\", message.Topic()))\n\n\t\terr := proto.Unmarshal(message.Payload(), reg)\n\t\tif err != nil {\n\t\t\te.logger.Error(\"unable to unmarshall \", zap.Error(err))\n\t\t\treturn\n\t\t}\n\n\t\tpid, err := peer.Decode(reg.Id)\n\t\tif err != nil {\n\t\t\te.logger.Error(\"unable to decode \", zap.Error(err))\n\t\t\treturn\n\t\t}\n\n\t\tmaddrs := make([]multiaddr.Multiaddr, len(reg.Addrs))\n\t\tfor i, addrBytes := range reg.Addrs {\n\t\t\tmaddrs[i], err = multiaddr.NewMultiaddrBytes(addrBytes)\n\t\t\tif err != nil {\n\t\t\t\te.logger.Error(\"unable to decode multiaddr bytes\", zap.Error(err))\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tvar fields []string\n\t\tfor _, maddr := range maddrs {\n\t\t\tfields = append(fields, maddr.String())\n\t\t}\n\n\t\te.logger.Debug(\"receiving a peer\", zap.String(\"topic\", reg.Ns), zap.String(\"peer\", pid.String()), zap.Strings(\"maddrs\", fields))\n\n\t\tinChan <- &registrationMessage{\n\t\t\tregistration: &rendezvous.Registration{\n\t\t\t\tPeer: peer.AddrInfo{\n\t\t\t\t\tID:    pid,\n\t\t\t\t\tAddrs: maddrs,\n\t\t\t\t},\n\t\t\t\tNs:  reg.Ns,\n\t\t\t\tTtl: int(reg.Ttl),\n\t\t\t},\n\t\t\tpsDetails: psDetails,\n\t\t}\n\t}, emitter.WithLast(1))\n}\n\nfunc (e *emitterClientManager) getClient(psDetails *EmitterPubSubSubscriptionDetails) (client *emitterClient, err error) {\n\tvar ok bool\n\tif client, ok = e.clients[psDetails.ServerAddr]; ok {\n\t\treturn\n\t}\n\n\tnoophandler := func(*emitter.Client, emitter.Message) {}\n\tcl, err := emitter.Connect(psDetails.ServerAddr, noophandler, emitter.WithLogger(e.logger.Named(\"cl\")))\n\tif err != nil {\n\t\treturn\n\t}\n\n\twrapCtx, cancel := context.WithCancel(context.Background())\n\tclient = &emitterClient{\n\t\tlogger: e.logger.Named(\"cl\"),\n\t\tctx:    wrapCtx,\n\t\tcancel: cancel,\n\t\tclient: cl,\n\t}\n\te.clients[psDetails.ServerAddr] = client\n\n\treturn\n}\n\nfunc (e *emitterClientManager) Subscribe(ctx context.Context, psDetailsStr string) (<-chan *rendezvous.Registration, error) {\n\tpsDetails := &EmitterPubSubSubscriptionDetails{}\n\n\terr := json.Unmarshal([]byte(psDetailsStr), psDetails)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to decode json: %w\", err)\n\t}\n\n\te.mu.Lock()\n\tdefer e.mu.Unlock()\n\n\tclient, err := e.getClient(psDetails)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to : %w\", err)\n\t}\n\n\tsliceName := makeOutChanName(psDetails.ServerAddr, psDetails.ChannelName)\n\tif _, ok := e.outChans[sliceName]; !ok {\n\t\te.logger.Debug(\"subscribing\",\n\t\t\tzap.String(\"channel\", psDetails.ChannelName), zap.String(\"target\", psDetails.ServerAddr))\n\n\t\tif err := client.subscribeToServerUpdates(e.inChan, psDetails); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"unable to subscribe to broker's channel: %w\", err)\n\t\t}\n\t}\n\n\tch := make(chan *rendezvous.Registration)\n\te.outChans[sliceName] = append(e.outChans[sliceName], ch)\n\tclient.usage++\n\n\tgo func() {\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\tcase <-client.ctx.Done():\n\t\t}\n\n\t\te.unsubscribe(psDetails, ch)\n\t\tclose(ch)\n\t}()\n\n\treturn ch, nil\n}\n\ntype EmitterClientOptions struct {\n\tLogger *zap.Logger\n}\n\nfunc NewEmitterClient(opts *EmitterClientOptions) SyncClient {\n\tif opts == nil {\n\t\topts = &EmitterClientOptions{}\n\t}\n\n\tif opts.Logger == nil {\n\t\topts.Logger = zap.NewNop()\n\t}\n\n\tctx, cancel := context.WithCancel(context.Background())\n\tmanager := &emitterClientManager{\n\t\tctx:      ctx,\n\t\tcancel:   cancel,\n\t\tclients:  map[string]*emitterClient{},\n\t\tlogger:   opts.Logger.Named(\"emitter\"),\n\t\toutChans: map[string][]chan *rendezvous.Registration{},\n\t\tinChan:   make(chan *registrationMessage),\n\t}\n\n\tmanager.logger.Debug(\"starting rdvp emitter client\")\n\tgo func() {\n\t\tfor {\n\t\t\tselect {\n\t\t\tcase <-ctx.Done():\n\t\t\t\tclose(manager.inChan)\n\t\t\t\treturn\n\t\t\tcase registration := <-manager.inChan:\n\t\t\t\toutChanName := makeOutChanName(registration.psDetails.ServerAddr, registration.psDetails.ChannelName)\n\n\t\t\t\tmanager.mu.Lock()\n\t\t\t\tchannels, ok := manager.outChans[outChanName]\n\t\t\t\tmanager.mu.Unlock()\n\t\t\t\tif !ok {\n\t\t\t\t\tmanager.logger.Warn(\"received an event for an unknown channel\", zap.String(\"channel\", registration.psDetails.ChannelName))\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tfor _, ch := range channels {\n\t\t\t\t\tch <- registration.registration\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}()\n\n\treturn manager\n}\n\nfunc (e *emitterClientManager) GetServiceType() string {\n\treturn EmitterServiceType\n}\n\nfunc makeOutChanName(serverAddr, channelName string) string {\n\treturn fmt.Sprintf(\"%s:%s\", serverAddr, channelName)\n}\n\nfunc (e *emitterClientManager) unsubscribe(details *EmitterPubSubSubscriptionDetails, ch chan *rendezvous.Registration) {\n\te.mu.Lock()\n\tdefer e.mu.Unlock()\n\n\tsliceName := makeOutChanName(details.ServerAddr, details.ChannelName)\n\tfor i, outChan := range e.outChans[sliceName] {\n\t\tif outChan == ch {\n\t\t\te.outChans[sliceName] = append(e.outChans[sliceName][:i], e.outChans[sliceName][i+1:]...)\n\t\t\tbreak\n\t\t}\n\t}\n\n\tclient, err := e.getClient(details)\n\tif err != nil {\n\t\te.logger.Error(\"unable to find client\", zap.Error(err))\n\t\treturn\n\t}\n\n\tif len(e.outChans[sliceName]) == 0 {\n\t\tif err := client.client.Unsubscribe(details.ReadKey, details.ChannelName); err != nil {\n\t\t\te.logger.Error(\"unable to unsubscribe from client\", zap.Error(err))\n\t\t}\n\t}\n\n\tclient.usage--\n\tif client.usage <= 0 {\n\t\te.clients[details.ServerAddr].cancel()\n\t\tdelete(e.clients, details.ServerAddr)\n\t}\n}\n"
  },
  {
    "path": "pkg/rendezvous/emitterio_sync_provider.go",
    "content": "package rendezvous\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"math/big\"\n\t\"time\"\n\n\temitter \"github.com/berty/emitter-go/v2\"\n\trendezvous \"github.com/berty/go-libp2p-rendezvous\"\n\tpb \"github.com/berty/go-libp2p-rendezvous/pb\"\n\t\"github.com/libp2p/go-libp2p/core/peer\"\n\t\"go.uber.org/zap\"\n\t\"google.golang.org/protobuf/proto\"\n)\n\nconst EmitterServiceType = \"emitter-io\"\n\ntype EmitterPubSub struct {\n\tclient     *emitter.Client\n\tadminKey   string\n\tserverAddr string\n\tpublicaddr string\n\tlogger     *zap.Logger\n}\n\ntype EmitterPubSubSubscriptionDetails struct {\n\tServerAddr  string\n\tReadKey     string\n\tChannelName string\n}\n\ntype EmitterOptions struct {\n\tLogger           *zap.Logger\n\tServerPublicAddr string\n\tEmitterOptions   []func(*emitter.Client)\n}\n\nfunc NewEmitterServer(serverAddr string, adminKey string, options *EmitterOptions) (*EmitterPubSub, error) {\n\tif options == nil {\n\t\toptions = &EmitterOptions{}\n\t}\n\n\tif options.ServerPublicAddr == \"\" {\n\t\toptions.ServerPublicAddr = serverAddr\n\t}\n\n\tif options.Logger == nil {\n\t\toptions.Logger = zap.NewNop()\n\t}\n\toptions.Logger = options.Logger.Named(\"emitter\")\n\n\tc, err := emitter.Connect(serverAddr, func(_ *emitter.Client, msg emitter.Message) {\n\t\toptions.Logger.Debug(\"emitter received msg\", zap.String(\"topic\", msg.Topic()))\n\t}, emitter.WithLogger(options.Logger.Named(\"cl\")))\n\tif err != nil {\n\t\toptions.Logger.Error(\"error on `Connect`\", zap.Error(err))\n\t}\n\n\toptions.Logger.Debug(\"connected to emitter\", zap.String(\"target\", serverAddr))\n\n\tps := &EmitterPubSub{\n\t\tclient:     c,\n\t\tadminKey:   adminKey,\n\t\tlogger:     options.Logger,\n\t\tpublicaddr: options.ServerPublicAddr,\n\t\tserverAddr: serverAddr,\n\t}\n\n\treturn ps, nil\n}\n\n// nolint:revive\nfunc (p *EmitterPubSub) Register(pid peer.ID, ns string, addrs [][]byte, ttlAsSeconds int, counter uint64) {\n\tp.logger.Debug(\"register\", zap.String(\"pid\", pid.String()), zap.String(\"ns\", ns))\n\n\tchannel := channelForRendezvousPoint(ns)\n\twriteKey, err := p.writeKeyForChannel(channel)\n\tif err != nil {\n\t\tp.logger.Error(\"unable to create topic for NS\", zap.Error(err))\n\t\treturn\n\t}\n\n\tdataToSend := &pb.RegistrationRecord{\n\t\tId:    pid.String(),\n\t\tAddrs: addrs,\n\t\tNs:    ns,\n\t\tTtl:   time.Now().Add(time.Duration(ttlAsSeconds) * time.Second).UnixMilli(),\n\t}\n\n\tmarshaled, err := proto.Marshal(dataToSend)\n\tif err != nil {\n\t\tp.logger.Error(\"unable to marshal proto\", zap.Error(err))\n\t\treturn\n\t}\n\n\tif err := p.client.PublishWithTTL(writeKey, channel, marshaled, ttlAsSeconds); err != nil {\n\t\tp.logger.Error(\"unable to publish on channel\", zap.Error(err))\n\t\treturn\n\t}\n\n\tp.logger.Debug(\"publishing done\", zap.String(\"pid\", pid.String()), zap.String(\"channel\", channel))\n}\n\nfunc (p *EmitterPubSub) Unregister(_ peer.ID, _ string) {\n\tp.logger.Debug(\"unsupported method unregister\")\n}\n\nfunc (p *EmitterPubSub) Subscribe(ns string) (string, error) {\n\tchannel := channelForRendezvousPoint(ns)\n\treadKey, err := p.readKeysForChannel(channel)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tjsonData, err := json.Marshal(&EmitterPubSubSubscriptionDetails{\n\t\tServerAddr:  p.publicaddr,\n\t\tReadKey:     readKey,\n\t\tChannelName: channel,\n\t})\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\treturn string(jsonData), nil\n}\n\nfunc (p *EmitterPubSub) GetServiceType() string {\n\treturn EmitterServiceType\n}\n\nfunc (p *EmitterPubSub) Close() error {\n\tp.client.Disconnect(time.Second)\n\treturn nil\n}\n\nfunc (p *EmitterPubSub) writeKeyForChannel(channelName string) (string, error) {\n\treturn p.keyForChannel(channelName, \"w\")\n}\n\nfunc (p *EmitterPubSub) readKeysForChannel(channelName string) (string, error) {\n\treturn p.keyForChannel(channelName, \"r\")\n}\n\nfunc (p *EmitterPubSub) keyForChannel(channelName string, permissions string) (string, error) {\n\tkey, err := p.client.GenerateKey(p.adminKey, channelName, permissions, int(time.Hour.Seconds()*24))\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"unable to generate key: %w\", err)\n\t}\n\n\treturn key, nil\n}\n\nfunc channelForRendezvousPoint(topic string) string {\n\t// force base62 encoded topic (without '+' and '#' that are wildcard in the mqtt world)\n\ttopic = toBase62(topic)\n\treturn fmt.Sprintf(\"rdvp/%s/\", topic)\n}\n\nfunc toBase62(topic string) string {\n\tvar i big.Int\n\ti.SetBytes([]byte(topic))\n\treturn i.Text(62)\n}\n\nvar (\n\t_ rendezvous.RendezvousSync             = (*EmitterPubSub)(nil)\n\t_ rendezvous.RendezvousSyncSubscribable = (*EmitterPubSub)(nil)\n)\n"
  },
  {
    "path": "pkg/rendezvous/emitterio_sync_test.go",
    "content": "package rendezvous_test\n\nimport (\n\t\"context\"\n\t\"os\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n\n\trendezvous \"github.com/berty/go-libp2p-rendezvous\"\n\tdb \"github.com/berty/go-libp2p-rendezvous/db/sqlcipher\"\n\t\"github.com/berty/go-libp2p-rendezvous/test_utils\"\n\t\"github.com/libp2p/go-libp2p/core/host\"\n\tmocknet \"github.com/libp2p/go-libp2p/p2p/net/mock\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/zap\"\n\n\tberty_rendezvous \"berty.tech/weshnet/v2/pkg/rendezvous\"\n)\n\nfunc MakeRendezvousServiceTest(ctx context.Context, host host.Host, path string, rzs ...rendezvous.RendezvousSync) (*rendezvous.RendezvousService, error) {\n\tdbi, err := db.OpenDB(ctx, path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn rendezvous.NewRendezvousService(host, dbi, rzs...), nil\n}\n\nfunc getEmitterRendezvousClients(ctx context.Context, t *testing.T, hosts []host.Host) []rendezvous.RendezvousClient {\n\tt.Helper()\n\n\tclients := make([]rendezvous.RendezvousClient, len(hosts)-1)\n\tfor i, host := range hosts[1:] {\n\t\tsyncClient := berty_rendezvous.NewEmitterClient(nil)\n\t\tt.Cleanup(func() {\n\t\t\tsyncClient.Close()\n\t\t})\n\t\tclients[i] = rendezvous.NewRendezvousClient(host, hosts[0].ID(), syncClient)\n\t}\n\treturn clients\n}\n\nfunc TestEmitterIOFlow(t *testing.T) {\n\t// @NOTE(gfanton): see tools/emitter-server to test it\n\t// TEST_EMITTER_SERVER_ADDR=<addr> TEST_EMITTER_ADMINKEY=<admin_key> go test .\n\n\tconst topic = \"foo1\"\n\n\tserverAddr := os.Getenv(\"TEST_EMITTER_SERVER_ADDR\")\n\tadminKey := os.Getenv(\"TEST_EMITTER_ADMINKEY\")\n\tif adminKey == \"\" || serverAddr == \"\" {\n\t\tt.Skip(\"cannot test emitter, no adminKey/serverAddr provided\")\n\t}\n\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\tmn := mocknet.New()\n\tdefer mn.Close()\n\n\t// Instantiate server and clients\n\thosts := test_utils.GetRendezvousHosts(t, ctx, mn, 4)\n\tfor _, h := range hosts {\n\t\tt.Cleanup(func() {\n\t\t\t_ = h.Close()\n\t\t})\n\t}\n\n\tlogger, err := zap.NewDevelopment()\n\trequire.NoError(t, err)\n\n\temitterPubSubSync, err := berty_rendezvous.NewEmitterServer(serverAddr, adminKey, &berty_rendezvous.EmitterOptions{\n\t\tLogger: logger.Named(\"emitter\"),\n\t})\n\trequire.NoError(t, err)\n\tdefer emitterPubSubSync.Close()\n\n\tsvc, err := MakeRendezvousServiceTest(ctx, hosts[0], \":memory:\", emitterPubSubSync)\n\trequire.NoError(t, err)\n\tdefer svc.DB.Close()\n\n\tclients := getEmitterRendezvousClients(ctx, t, hosts)\n\n\tregFound := int64(0)\n\twg := sync.WaitGroup{}\n\n\tconst announcementCount = 5\n\n\tfor _, client := range clients[1:] {\n\t\twg.Add(1)\n\t\tctx, cancel := context.WithTimeout(ctx, time.Second*5)\n\t\tch, err := client.DiscoverSubscribe(ctx, topic)\n\t\trequire.NoError(t, err)\n\n\t\tgo func() {\n\t\t\tregFoundForPeer := 0\n\n\t\t\tdefer cancel()\n\t\t\tdefer wg.Done()\n\n\t\t\tfor p := range ch {\n\t\t\t\tif test_utils.CheckPeerInfo(t, p, hosts[2], false) == true {\n\t\t\t\t\tregFoundForPeer++\n\t\t\t\t\tatomic.AddInt64(&regFound, 1)\n\t\t\t\t}\n\n\t\t\t\tif regFoundForPeer == announcementCount {\n\t\t\t\t\tgo func() {\n\t\t\t\t\t\t// this allows more events to be received\n\t\t\t\t\t\ttime.Sleep(time.Millisecond * 500)\n\t\t\t\t\t\tcancel()\n\t\t\t\t\t}()\n\t\t\t\t}\n\t\t\t}\n\t\t}()\n\t}\n\n\tfor i := 0; i < announcementCount; i++ {\n\t\t_, err = clients[1].Register(ctx, topic, rendezvous.DefaultTTL)\n\t\trequire.NoError(t, err)\n\t}\n\n\twg.Wait()\n\tif regFound != int64(len(clients[1:]))*announcementCount {\n\t\trequire.FailNowf(t, \"number of records doesn't match\", \"expected %d records to be found got %d\", int64(len(clients[1:])), regFound)\n\t}\n}\n"
  },
  {
    "path": "pkg/rendezvous/rendezvous.go",
    "content": "package rendezvous\n\nimport (\n\t\"crypto/hmac\"\n\t\"crypto/sha256\"\n\t\"encoding/binary\"\n\t\"time\"\n)\n\nconst DefaultRotationInterval = time.Hour * 24\n\nfunc RoundTimePeriod(date time.Time, interval time.Duration) time.Time {\n\tif interval < 0 {\n\t\tinterval = -interval\n\t}\n\n\tintervalSeconds := int64(interval.Seconds())\n\n\tperiodsElapsed := date.Unix() / intervalSeconds\n\ttotalTime := periodsElapsed * intervalSeconds\n\n\treturn time.Unix(totalTime, 0).In(date.Location())\n}\n\nfunc NextTimePeriod(date time.Time, interval time.Duration) time.Time {\n\tif interval < 0 {\n\t\tinterval = -interval\n\t}\n\n\treturn RoundTimePeriod(date, interval).Add(interval)\n}\n\nfunc GenerateRendezvousPointForPeriod(topic, seed []byte, date time.Time) []byte {\n\tbuf := make([]byte, 8)\n\tmac := hmac.New(sha256.New, append(topic, seed...))\n\tbinary.BigEndian.PutUint64(buf, uint64(date.Unix()))\n\n\t// hash.Hash.Write never returns an error, as documented at\n\t// https://pkg.go.dev/hash#Hash\n\tmac.Write(buf) //nolint:errcheck\n\tsum := mac.Sum(nil)\n\n\treturn sum\n}\n"
  },
  {
    "path": "pkg/rendezvous/rendezvous_test.go",
    "content": "package rendezvous_test\n\nimport (\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\t\"berty.tech/weshnet/v2/pkg/rendezvous\"\n)\n\nfunc TestRoundTimePeriod_Next(t *testing.T) {\n\tcases := []struct {\n\t\ttime   time.Time\n\t\tperiod time.Duration\n\t\tout    time.Time\n\t\tnext   time.Time\n\t}{\n\t\t{\n\t\t\ttime:   time.Date(2020, 4, 10, 12, 0, 0, 0, time.UTC),\n\t\t\tperiod: time.Hour,\n\t\t\tout:    time.Date(2020, 4, 10, 12, 0, 0, 0, time.UTC),\n\t\t\tnext:   time.Date(2020, 4, 10, 13, 0, 0, 0, time.UTC),\n\t\t},\n\t\t{\n\t\t\ttime:   time.Date(2020, 4, 10, 12, 0, 0, 0, time.UTC),\n\t\t\tperiod: -time.Hour,\n\t\t\tout:    time.Date(2020, 4, 10, 12, 0, 0, 0, time.UTC),\n\t\t\tnext:   time.Date(2020, 4, 10, 13, 0, 0, 0, time.UTC),\n\t\t},\n\t\t{\n\t\t\ttime:   time.Date(2020, 4, 10, 12, 34, 56, 0, time.UTC),\n\t\t\tperiod: time.Hour,\n\t\t\tout:    time.Date(2020, 4, 10, 12, 0, 0, 0, time.UTC),\n\t\t\tnext:   time.Date(2020, 4, 10, 13, 0, 0, 0, time.UTC),\n\t\t},\n\t}\n\n\tfor i, tc := range cases {\n\t\tt.Run(fmt.Sprintf(\"tc: %d\", i), func(t *testing.T) {\n\t\t\trounded := rendezvous.RoundTimePeriod(tc.time, tc.period)\n\t\t\trequire.Equal(t, tc.out, rounded)\n\n\t\t\tnext := rendezvous.NextTimePeriod(tc.time, tc.period)\n\t\t\trequire.Equal(t, tc.next, next)\n\t\t})\n\t}\n}\n\nfunc TestGenerateRendezvousPointForPeriod(t *testing.T) {\n\tbaseTimeA := time.Date(2020, 4, 10, 12, 0, 0, 0, time.UTC)\n\tbaseTimeB := time.Date(2020, 4, 10, 13, 0, 0, 0, time.UTC)\n\n\tcases := []struct {\n\t\ttopic    []byte\n\t\tseed     []byte\n\t\texpected string // as hex string\n\t\ttime     time.Time\n\t}{\n\t\t{\n\t\t\ttopic:    []byte(\"topicA\"),\n\t\t\tseed:     []byte(\"seedA\"),\n\t\t\texpected: \"8b7fdc831ca90f78995f32d8b9cf7bc8682a7fc250fe13a9b7c5c0851a3b8cbc\",\n\t\t\ttime:     baseTimeA,\n\t\t},\n\t\t{\n\t\t\ttopic:    []byte(\"topicA\"),\n\t\t\tseed:     []byte(\"seedA\"),\n\t\t\texpected: \"ec86e0cb471195733ebbeb04277aafcfc60a19c09195cf04e60b50857465c27f\",\n\t\t\ttime:     baseTimeB,\n\t\t},\n\t\t{\n\t\t\ttopic:    []byte(\"topicA\"),\n\t\t\tseed:     []byte(\"seedB\"),\n\t\t\texpected: \"f87f5dfc4e8a68be75d6008ab3aa0a4295b6049e9ec21f03d0c1410895171683\",\n\t\t\ttime:     baseTimeA,\n\t\t},\n\t\t{\n\t\t\ttopic:    []byte(\"topicB\"),\n\t\t\tseed:     []byte(\"seedA\"),\n\t\t\texpected: \"6350bd507198a9acb816dcadae8c5c6ff6c96ee6c9606c8ebe15c1f645ac4c4e\",\n\t\t\ttime:     baseTimeA,\n\t\t},\n\t}\n\n\tfor i, tc := range cases {\n\t\tt.Run(fmt.Sprintf(\"tc: %d\", i), func(t *testing.T) {\n\t\t\tpoint := rendezvous.GenerateRendezvousPointForPeriod(tc.topic, tc.seed, tc.time)\n\t\t\trequire.Equal(t, tc.expected, hex.EncodeToString(point))\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/rendezvous/rotation.go",
    "content": "package rendezvous\n\nimport (\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"sync\"\n\t\"time\"\n)\n\nvar (\n\tRotationGracePeriod  = time.Minute * 10\n\tMinimumDelayRotation = time.Minute\n)\n\ntype RotationInterval struct {\n\tinterval time.Duration\n\n\tcacheTopics    map[string]*Point\n\tcacheRotations map[string]*Point\n\tmuCache        sync.RWMutex\n}\n\nfunc NewStaticRotationInterval() *RotationInterval {\n\t// from https://stackoverflow.com/a/32620397\n\tmaxTime := time.Unix(1<<63-62135596801, 999999999)\n\treturn NewRotationInterval(time.Until(maxTime))\n}\n\nfunc NewRotationInterval(interval time.Duration) *RotationInterval {\n\treturn &RotationInterval{\n\t\tinterval:       interval,\n\t\tcacheTopics:    make(map[string]*Point),\n\t\tcacheRotations: make(map[string]*Point),\n\t}\n}\n\nfunc (r *RotationInterval) RegisterRotation(at time.Time, topic string, seed []byte) {\n\tpoint := r.NewRendezvousPointForPeriod(at, topic, seed)\n\tr.muCache.Lock()\n\tr.registerPoint(point)\n\tr.muCache.Unlock()\n}\n\nfunc (r *RotationInterval) RoundTimePeriod(at time.Time) time.Time {\n\treturn RoundTimePeriod(at, r.interval)\n}\n\nfunc (r *RotationInterval) NextTimePeriod(at time.Time) time.Time {\n\treturn NextTimePeriod(at, r.interval)\n}\n\nfunc (r *RotationInterval) PointForRawRotation(rotation []byte) (*Point, error) {\n\treturn r.PointForRotation(base64.StdEncoding.EncodeToString(rotation))\n}\n\nfunc (r *RotationInterval) PointForRotation(rotation string) (*Point, error) {\n\tr.muCache.Lock()\n\tdefer r.muCache.Unlock()\n\n\tif point, ok := r.cacheRotations[rotation]; ok {\n\t\tif point.IsExpired() {\n\t\t\tpoint = r.rotate(point, DefaultRotationInterval)\n\t\t}\n\n\t\treturn point, nil\n\t}\n\n\treturn nil, fmt.Errorf(\"unable to get rendez vous, no matching rotation registered\")\n}\n\nfunc (r *RotationInterval) PointForTopic(topic string) (*Point, error) {\n\tr.muCache.Lock()\n\tdefer r.muCache.Unlock()\n\n\tif point, ok := r.cacheTopics[topic]; ok {\n\t\tif point.IsExpired() {\n\t\t\tpoint = r.rotate(point, DefaultRotationInterval)\n\t\t}\n\n\t\treturn point, nil\n\t}\n\n\treturn nil, fmt.Errorf(\"unable to get rendezvous point, no matching topic registered\")\n}\n\nfunc (r *RotationInterval) NewRendezvousPointForPeriod(at time.Time, topic string, seed []byte) (point *Point) {\n\tat = r.RoundTimePeriod(at)\n\trotation := GenerateRendezvousPointForPeriod([]byte(topic), seed, at)\n\n\tnext := r.NextTimePeriod(at)\n\treturn &Point{\n\t\trp:       r,\n\t\trotation: rotation,\n\t\ttopic:    topic,\n\t\tseed:     seed,\n\t\tdeadline: next,\n\t}\n}\n\nfunc (r *RotationInterval) registerPoint(point *Point) {\n\tkeytopic, keyrotation := point.keys()\n\tr.cacheTopics[keytopic] = point\n\tr.cacheRotations[keyrotation] = point\n}\n\nfunc (r *RotationInterval) rotate(old *Point, graceperiod time.Duration) *Point {\n\tnewPoint := old.NextPoint()\n\n\t// register new point\n\tr.registerPoint(newPoint)\n\n\tcleanuptime := max(time.Until(newPoint.Deadline().Add(graceperiod)), 0)\n\t// cleanup after the grace period\n\ttime.AfterFunc(cleanuptime, func() {\n\t\tr.muCache.Lock()\n\t\t_, keyrotation := old.keys()\n\t\tdelete(r.cacheRotations, keyrotation)\n\t\tr.muCache.Unlock()\n\t})\n\n\treturn newPoint\n}\n\ntype Point struct {\n\trp       *RotationInterval\n\ttopic    string\n\trotation []byte\n\tseed     []byte\n\tdeadline time.Time\n}\n\nfunc (p *Point) NextPoint() *Point {\n\tif p.IsExpired() {\n\t\treturn p.rp.NewRendezvousPointForPeriod(time.Now(), p.topic, p.seed)\n\t}\n\n\treturn p.rp.NewRendezvousPointForPeriod(p.deadline.Add(time.Second), p.topic, p.seed)\n}\n\nfunc (p *Point) Seed() []byte {\n\treturn p.seed\n}\n\nfunc (p *Point) keys() (topic string, rotation string) {\n\ttopic = p.Topic()\n\trotation = p.RotationTopic()\n\treturn\n}\n\nfunc (p *Point) Topic() string {\n\treturn p.topic\n}\n\nfunc (p *Point) RawTopic() []byte {\n\treturn []byte(p.topic)\n}\n\nfunc (p *Point) RotationTopic() string {\n\treturn base64.StdEncoding.EncodeToString(p.rotation)\n}\n\nfunc (p *Point) RawRotationTopic() []byte {\n\treturn p.rotation\n}\n\nfunc (p *Point) Deadline() time.Time {\n\treturn p.deadline\n}\n\nfunc (p *Point) TTL() time.Duration {\n\treturn time.Until(p.deadline)\n}\n\nfunc (p *Point) IsExpired() bool {\n\treturn p.TTL() > 0\n}\n"
  },
  {
    "path": "pkg/replicationtypes/bertyreplication.pb.go",
    "content": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.34.2\n// \tprotoc        (unknown)\n// source: replicationtypes/bertyreplication.proto\n\npackage replicationtypes\n\nimport (\n\tprotocoltypes \"berty.tech/weshnet/v2/pkg/protocoltypes\"\n\t_ \"github.com/srikrsna/protoc-gen-gotag/tagger\"\n\tprotoreflect \"google.golang.org/protobuf/reflect/protoreflect\"\n\tprotoimpl \"google.golang.org/protobuf/runtime/protoimpl\"\n\treflect \"reflect\"\n\tsync \"sync\"\n)\n\nconst (\n\t// Verify that this generated code is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)\n\t// Verify that runtime/protoimpl is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)\n)\n\ntype ReplicatedGroup struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tPublicKey            string `protobuf:\"bytes,1,opt,name=public_key,json=publicKey,proto3\" json:\"public_key,omitempty\" gorm:\"primaryKey\"`\n\tSignPub              string `protobuf:\"bytes,2,opt,name=sign_pub,json=signPub,proto3\" json:\"sign_pub,omitempty\"`\n\tLinkKey              string `protobuf:\"bytes,3,opt,name=link_key,json=linkKey,proto3\" json:\"link_key,omitempty\"`\n\tCreatedAt            int64  `protobuf:\"varint,100,opt,name=created_at,json=createdAt,proto3\" json:\"created_at,omitempty\"`\n\tUpdatedAt            int64  `protobuf:\"varint,101,opt,name=updated_at,json=updatedAt,proto3\" json:\"updated_at,omitempty\"`\n\tMetadataEntriesCount int64  `protobuf:\"varint,102,opt,name=metadata_entries_count,json=metadataEntriesCount,proto3\" json:\"metadata_entries_count,omitempty\"`\n\tMetadataLatestHead   string `protobuf:\"bytes,103,opt,name=metadata_latest_head,json=metadataLatestHead,proto3\" json:\"metadata_latest_head,omitempty\"`\n\tMessageEntriesCount  int64  `protobuf:\"varint,104,opt,name=message_entries_count,json=messageEntriesCount,proto3\" json:\"message_entries_count,omitempty\"`\n\tMessageLatestHead    string `protobuf:\"bytes,105,opt,name=message_latest_head,json=messageLatestHead,proto3\" json:\"message_latest_head,omitempty\"`\n}\n\nfunc (x *ReplicatedGroup) Reset() {\n\t*x = ReplicatedGroup{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_replicationtypes_bertyreplication_proto_msgTypes[0]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ReplicatedGroup) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ReplicatedGroup) ProtoMessage() {}\n\nfunc (x *ReplicatedGroup) ProtoReflect() protoreflect.Message {\n\tmi := &file_replicationtypes_bertyreplication_proto_msgTypes[0]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ReplicatedGroup.ProtoReflect.Descriptor instead.\nfunc (*ReplicatedGroup) Descriptor() ([]byte, []int) {\n\treturn file_replicationtypes_bertyreplication_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *ReplicatedGroup) GetPublicKey() string {\n\tif x != nil {\n\t\treturn x.PublicKey\n\t}\n\treturn \"\"\n}\n\nfunc (x *ReplicatedGroup) GetSignPub() string {\n\tif x != nil {\n\t\treturn x.SignPub\n\t}\n\treturn \"\"\n}\n\nfunc (x *ReplicatedGroup) GetLinkKey() string {\n\tif x != nil {\n\t\treturn x.LinkKey\n\t}\n\treturn \"\"\n}\n\nfunc (x *ReplicatedGroup) GetCreatedAt() int64 {\n\tif x != nil {\n\t\treturn x.CreatedAt\n\t}\n\treturn 0\n}\n\nfunc (x *ReplicatedGroup) GetUpdatedAt() int64 {\n\tif x != nil {\n\t\treturn x.UpdatedAt\n\t}\n\treturn 0\n}\n\nfunc (x *ReplicatedGroup) GetMetadataEntriesCount() int64 {\n\tif x != nil {\n\t\treturn x.MetadataEntriesCount\n\t}\n\treturn 0\n}\n\nfunc (x *ReplicatedGroup) GetMetadataLatestHead() string {\n\tif x != nil {\n\t\treturn x.MetadataLatestHead\n\t}\n\treturn \"\"\n}\n\nfunc (x *ReplicatedGroup) GetMessageEntriesCount() int64 {\n\tif x != nil {\n\t\treturn x.MessageEntriesCount\n\t}\n\treturn 0\n}\n\nfunc (x *ReplicatedGroup) GetMessageLatestHead() string {\n\tif x != nil {\n\t\treturn x.MessageLatestHead\n\t}\n\treturn \"\"\n}\n\ntype ReplicatedGroupToken struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tReplicatedGroupPublicKey string           `protobuf:\"bytes,1,opt,name=replicated_group_public_key,json=replicatedGroupPublicKey,proto3\" json:\"replicated_group_public_key,omitempty\" gorm:\"index;primaryKey;autoIncrement:false\"`\n\tReplicatedGroup          *ReplicatedGroup `protobuf:\"bytes,2,opt,name=replicated_group,json=replicatedGroup,proto3\" json:\"replicated_group,omitempty\"`\n\tTokenIssuer              string           `protobuf:\"bytes,3,opt,name=token_issuer,json=tokenIssuer,proto3\" json:\"token_issuer,omitempty\" gorm:\"primaryKey;autoIncrement:false\"`\n\tTokenId                  string           `protobuf:\"bytes,4,opt,name=token_id,json=tokenId,proto3\" json:\"token_id,omitempty\" gorm:\"primaryKey;autoIncrement:false\"`\n\tCreatedAt                int64            `protobuf:\"varint,5,opt,name=created_at,json=createdAt,proto3\" json:\"created_at,omitempty\"`\n}\n\nfunc (x *ReplicatedGroupToken) Reset() {\n\t*x = ReplicatedGroupToken{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_replicationtypes_bertyreplication_proto_msgTypes[1]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ReplicatedGroupToken) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ReplicatedGroupToken) ProtoMessage() {}\n\nfunc (x *ReplicatedGroupToken) ProtoReflect() protoreflect.Message {\n\tmi := &file_replicationtypes_bertyreplication_proto_msgTypes[1]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ReplicatedGroupToken.ProtoReflect.Descriptor instead.\nfunc (*ReplicatedGroupToken) Descriptor() ([]byte, []int) {\n\treturn file_replicationtypes_bertyreplication_proto_rawDescGZIP(), []int{1}\n}\n\nfunc (x *ReplicatedGroupToken) GetReplicatedGroupPublicKey() string {\n\tif x != nil {\n\t\treturn x.ReplicatedGroupPublicKey\n\t}\n\treturn \"\"\n}\n\nfunc (x *ReplicatedGroupToken) GetReplicatedGroup() *ReplicatedGroup {\n\tif x != nil {\n\t\treturn x.ReplicatedGroup\n\t}\n\treturn nil\n}\n\nfunc (x *ReplicatedGroupToken) GetTokenIssuer() string {\n\tif x != nil {\n\t\treturn x.TokenIssuer\n\t}\n\treturn \"\"\n}\n\nfunc (x *ReplicatedGroupToken) GetTokenId() string {\n\tif x != nil {\n\t\treturn x.TokenId\n\t}\n\treturn \"\"\n}\n\nfunc (x *ReplicatedGroupToken) GetCreatedAt() int64 {\n\tif x != nil {\n\t\treturn x.CreatedAt\n\t}\n\treturn 0\n}\n\ntype ReplicationServiceReplicateGroup struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *ReplicationServiceReplicateGroup) Reset() {\n\t*x = ReplicationServiceReplicateGroup{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_replicationtypes_bertyreplication_proto_msgTypes[2]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ReplicationServiceReplicateGroup) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ReplicationServiceReplicateGroup) ProtoMessage() {}\n\nfunc (x *ReplicationServiceReplicateGroup) ProtoReflect() protoreflect.Message {\n\tmi := &file_replicationtypes_bertyreplication_proto_msgTypes[2]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ReplicationServiceReplicateGroup.ProtoReflect.Descriptor instead.\nfunc (*ReplicationServiceReplicateGroup) Descriptor() ([]byte, []int) {\n\treturn file_replicationtypes_bertyreplication_proto_rawDescGZIP(), []int{2}\n}\n\ntype ReplicateGlobalStats struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *ReplicateGlobalStats) Reset() {\n\t*x = ReplicateGlobalStats{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_replicationtypes_bertyreplication_proto_msgTypes[3]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ReplicateGlobalStats) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ReplicateGlobalStats) ProtoMessage() {}\n\nfunc (x *ReplicateGlobalStats) ProtoReflect() protoreflect.Message {\n\tmi := &file_replicationtypes_bertyreplication_proto_msgTypes[3]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ReplicateGlobalStats.ProtoReflect.Descriptor instead.\nfunc (*ReplicateGlobalStats) Descriptor() ([]byte, []int) {\n\treturn file_replicationtypes_bertyreplication_proto_rawDescGZIP(), []int{3}\n}\n\ntype ReplicateGroupStats struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *ReplicateGroupStats) Reset() {\n\t*x = ReplicateGroupStats{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_replicationtypes_bertyreplication_proto_msgTypes[4]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ReplicateGroupStats) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ReplicateGroupStats) ProtoMessage() {}\n\nfunc (x *ReplicateGroupStats) ProtoReflect() protoreflect.Message {\n\tmi := &file_replicationtypes_bertyreplication_proto_msgTypes[4]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ReplicateGroupStats.ProtoReflect.Descriptor instead.\nfunc (*ReplicateGroupStats) Descriptor() ([]byte, []int) {\n\treturn file_replicationtypes_bertyreplication_proto_rawDescGZIP(), []int{4}\n}\n\ntype ReplicationServiceReplicateGroup_Request struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tGroup *protocoltypes.Group `protobuf:\"bytes,1,opt,name=group,proto3\" json:\"group,omitempty\"`\n}\n\nfunc (x *ReplicationServiceReplicateGroup_Request) Reset() {\n\t*x = ReplicationServiceReplicateGroup_Request{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_replicationtypes_bertyreplication_proto_msgTypes[5]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ReplicationServiceReplicateGroup_Request) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ReplicationServiceReplicateGroup_Request) ProtoMessage() {}\n\nfunc (x *ReplicationServiceReplicateGroup_Request) ProtoReflect() protoreflect.Message {\n\tmi := &file_replicationtypes_bertyreplication_proto_msgTypes[5]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ReplicationServiceReplicateGroup_Request.ProtoReflect.Descriptor instead.\nfunc (*ReplicationServiceReplicateGroup_Request) Descriptor() ([]byte, []int) {\n\treturn file_replicationtypes_bertyreplication_proto_rawDescGZIP(), []int{2, 0}\n}\n\nfunc (x *ReplicationServiceReplicateGroup_Request) GetGroup() *protocoltypes.Group {\n\tif x != nil {\n\t\treturn x.Group\n\t}\n\treturn nil\n}\n\ntype ReplicationServiceReplicateGroup_Reply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tOk bool `protobuf:\"varint,1,opt,name=ok,proto3\" json:\"ok,omitempty\"`\n}\n\nfunc (x *ReplicationServiceReplicateGroup_Reply) Reset() {\n\t*x = ReplicationServiceReplicateGroup_Reply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_replicationtypes_bertyreplication_proto_msgTypes[6]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ReplicationServiceReplicateGroup_Reply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ReplicationServiceReplicateGroup_Reply) ProtoMessage() {}\n\nfunc (x *ReplicationServiceReplicateGroup_Reply) ProtoReflect() protoreflect.Message {\n\tmi := &file_replicationtypes_bertyreplication_proto_msgTypes[6]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ReplicationServiceReplicateGroup_Reply.ProtoReflect.Descriptor instead.\nfunc (*ReplicationServiceReplicateGroup_Reply) Descriptor() ([]byte, []int) {\n\treturn file_replicationtypes_bertyreplication_proto_rawDescGZIP(), []int{2, 1}\n}\n\nfunc (x *ReplicationServiceReplicateGroup_Reply) GetOk() bool {\n\tif x != nil {\n\t\treturn x.Ok\n\t}\n\treturn false\n}\n\ntype ReplicateGlobalStats_Request struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *ReplicateGlobalStats_Request) Reset() {\n\t*x = ReplicateGlobalStats_Request{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_replicationtypes_bertyreplication_proto_msgTypes[7]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ReplicateGlobalStats_Request) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ReplicateGlobalStats_Request) ProtoMessage() {}\n\nfunc (x *ReplicateGlobalStats_Request) ProtoReflect() protoreflect.Message {\n\tmi := &file_replicationtypes_bertyreplication_proto_msgTypes[7]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ReplicateGlobalStats_Request.ProtoReflect.Descriptor instead.\nfunc (*ReplicateGlobalStats_Request) Descriptor() ([]byte, []int) {\n\treturn file_replicationtypes_bertyreplication_proto_rawDescGZIP(), []int{3, 0}\n}\n\ntype ReplicateGlobalStats_Reply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tStartedAt            int64 `protobuf:\"varint,1,opt,name=started_at,json=startedAt,proto3\" json:\"started_at,omitempty\"`\n\tReplicatedGroups     int64 `protobuf:\"varint,2,opt,name=replicated_groups,json=replicatedGroups,proto3\" json:\"replicated_groups,omitempty\"`\n\tTotalMetadataEntries int64 `protobuf:\"varint,3,opt,name=total_metadata_entries,json=totalMetadataEntries,proto3\" json:\"total_metadata_entries,omitempty\"`\n\tTotalMessageEntries  int64 `protobuf:\"varint,4,opt,name=total_message_entries,json=totalMessageEntries,proto3\" json:\"total_message_entries,omitempty\"`\n}\n\nfunc (x *ReplicateGlobalStats_Reply) Reset() {\n\t*x = ReplicateGlobalStats_Reply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_replicationtypes_bertyreplication_proto_msgTypes[8]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ReplicateGlobalStats_Reply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ReplicateGlobalStats_Reply) ProtoMessage() {}\n\nfunc (x *ReplicateGlobalStats_Reply) ProtoReflect() protoreflect.Message {\n\tmi := &file_replicationtypes_bertyreplication_proto_msgTypes[8]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ReplicateGlobalStats_Reply.ProtoReflect.Descriptor instead.\nfunc (*ReplicateGlobalStats_Reply) Descriptor() ([]byte, []int) {\n\treturn file_replicationtypes_bertyreplication_proto_rawDescGZIP(), []int{3, 1}\n}\n\nfunc (x *ReplicateGlobalStats_Reply) GetStartedAt() int64 {\n\tif x != nil {\n\t\treturn x.StartedAt\n\t}\n\treturn 0\n}\n\nfunc (x *ReplicateGlobalStats_Reply) GetReplicatedGroups() int64 {\n\tif x != nil {\n\t\treturn x.ReplicatedGroups\n\t}\n\treturn 0\n}\n\nfunc (x *ReplicateGlobalStats_Reply) GetTotalMetadataEntries() int64 {\n\tif x != nil {\n\t\treturn x.TotalMetadataEntries\n\t}\n\treturn 0\n}\n\nfunc (x *ReplicateGlobalStats_Reply) GetTotalMessageEntries() int64 {\n\tif x != nil {\n\t\treturn x.TotalMessageEntries\n\t}\n\treturn 0\n}\n\ntype ReplicateGroupStats_Request struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tGroupPublicKey string `protobuf:\"bytes,1,opt,name=group_public_key,json=groupPublicKey,proto3\" json:\"group_public_key,omitempty\"`\n}\n\nfunc (x *ReplicateGroupStats_Request) Reset() {\n\t*x = ReplicateGroupStats_Request{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_replicationtypes_bertyreplication_proto_msgTypes[9]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ReplicateGroupStats_Request) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ReplicateGroupStats_Request) ProtoMessage() {}\n\nfunc (x *ReplicateGroupStats_Request) ProtoReflect() protoreflect.Message {\n\tmi := &file_replicationtypes_bertyreplication_proto_msgTypes[9]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ReplicateGroupStats_Request.ProtoReflect.Descriptor instead.\nfunc (*ReplicateGroupStats_Request) Descriptor() ([]byte, []int) {\n\treturn file_replicationtypes_bertyreplication_proto_rawDescGZIP(), []int{4, 0}\n}\n\nfunc (x *ReplicateGroupStats_Request) GetGroupPublicKey() string {\n\tif x != nil {\n\t\treturn x.GroupPublicKey\n\t}\n\treturn \"\"\n}\n\ntype ReplicateGroupStats_Reply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tGroup *ReplicatedGroup `protobuf:\"bytes,1,opt,name=group,proto3\" json:\"group,omitempty\"`\n}\n\nfunc (x *ReplicateGroupStats_Reply) Reset() {\n\t*x = ReplicateGroupStats_Reply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_replicationtypes_bertyreplication_proto_msgTypes[10]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ReplicateGroupStats_Reply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ReplicateGroupStats_Reply) ProtoMessage() {}\n\nfunc (x *ReplicateGroupStats_Reply) ProtoReflect() protoreflect.Message {\n\tmi := &file_replicationtypes_bertyreplication_proto_msgTypes[10]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ReplicateGroupStats_Reply.ProtoReflect.Descriptor instead.\nfunc (*ReplicateGroupStats_Reply) Descriptor() ([]byte, []int) {\n\treturn file_replicationtypes_bertyreplication_proto_rawDescGZIP(), []int{4, 1}\n}\n\nfunc (x *ReplicateGroupStats_Reply) GetGroup() *ReplicatedGroup {\n\tif x != nil {\n\t\treturn x.Group\n\t}\n\treturn nil\n}\n\nvar File_replicationtypes_bertyreplication_proto protoreflect.FileDescriptor\n\nvar file_replicationtypes_bertyreplication_proto_rawDesc = []byte{\n\t0x0a, 0x27, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x74, 0x79, 0x70,\n\t0x65, 0x73, 0x2f, 0x62, 0x65, 0x72, 0x74, 0x79, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74,\n\t0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x16, 0x77, 0x65, 0x73, 0x68, 0x6e,\n\t0x65, 0x74, 0x2e, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76,\n\t0x31, 0x1a, 0x13, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x74, 0x79, 0x70, 0x65, 0x73,\n\t0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x13, 0x74, 0x61, 0x67, 0x67, 0x65, 0x72, 0x2f, 0x74,\n\t0x61, 0x67, 0x67, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x88, 0x03, 0x0a, 0x0f,\n\t0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12,\n\t0x35, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20,\n\t0x01, 0x28, 0x09, 0x42, 0x16, 0x9a, 0x84, 0x9e, 0x03, 0x11, 0x67, 0x6f, 0x72, 0x6d, 0x3a, 0x22,\n\t0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x4b, 0x65, 0x79, 0x22, 0x52, 0x09, 0x70, 0x75, 0x62,\n\t0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x69, 0x67, 0x6e, 0x5f, 0x70,\n\t0x75, 0x62, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x69, 0x67, 0x6e, 0x50, 0x75,\n\t0x62, 0x12, 0x19, 0x0a, 0x08, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20,\n\t0x01, 0x28, 0x09, 0x52, 0x07, 0x6c, 0x69, 0x6e, 0x6b, 0x4b, 0x65, 0x79, 0x12, 0x1d, 0x0a, 0x0a,\n\t0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x64, 0x20, 0x01, 0x28, 0x03,\n\t0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x75,\n\t0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x65, 0x20, 0x01, 0x28, 0x03, 0x52,\n\t0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x34, 0x0a, 0x16, 0x6d, 0x65,\n\t0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x5f, 0x63,\n\t0x6f, 0x75, 0x6e, 0x74, 0x18, 0x66, 0x20, 0x01, 0x28, 0x03, 0x52, 0x14, 0x6d, 0x65, 0x74, 0x61,\n\t0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74,\n\t0x12, 0x30, 0x0a, 0x14, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x6c, 0x61, 0x74,\n\t0x65, 0x73, 0x74, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x18, 0x67, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12,\n\t0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x4c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x48, 0x65,\n\t0x61, 0x64, 0x12, 0x32, 0x0a, 0x15, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x65, 0x6e,\n\t0x74, 0x72, 0x69, 0x65, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x68, 0x20, 0x01, 0x28,\n\t0x03, 0x52, 0x13, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65,\n\t0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67,\n\t0x65, 0x5f, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x18, 0x69, 0x20,\n\t0x01, 0x28, 0x09, 0x52, 0x11, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4c, 0x61, 0x74, 0x65,\n\t0x73, 0x74, 0x48, 0x65, 0x61, 0x64, 0x22, 0x90, 0x03, 0x0a, 0x14, 0x52, 0x65, 0x70, 0x6c, 0x69,\n\t0x63, 0x61, 0x74, 0x65, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12,\n\t0x6f, 0x0a, 0x1b, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x67, 0x72,\n\t0x6f, 0x75, 0x70, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01,\n\t0x20, 0x01, 0x28, 0x09, 0x42, 0x30, 0x9a, 0x84, 0x9e, 0x03, 0x2b, 0x67, 0x6f, 0x72, 0x6d, 0x3a,\n\t0x22, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x3b, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x4b, 0x65,\n\t0x79, 0x3b, 0x61, 0x75, 0x74, 0x6f, 0x49, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x3a,\n\t0x66, 0x61, 0x6c, 0x73, 0x65, 0x22, 0x52, 0x18, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74,\n\t0x65, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79,\n\t0x12, 0x52, 0x0a, 0x10, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x67,\n\t0x72, 0x6f, 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x77, 0x65, 0x73,\n\t0x68, 0x6e, 0x65, 0x74, 0x2e, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,\n\t0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x47, 0x72,\n\t0x6f, 0x75, 0x70, 0x52, 0x0f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x47,\n\t0x72, 0x6f, 0x75, 0x70, 0x12, 0x4d, 0x0a, 0x0c, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x69, 0x73,\n\t0x73, 0x75, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x2a, 0x9a, 0x84, 0x9e, 0x03,\n\t0x25, 0x67, 0x6f, 0x72, 0x6d, 0x3a, 0x22, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x4b, 0x65,\n\t0x79, 0x3b, 0x61, 0x75, 0x74, 0x6f, 0x49, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x3a,\n\t0x66, 0x61, 0x6c, 0x73, 0x65, 0x22, 0x52, 0x0b, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x49, 0x73, 0x73,\n\t0x75, 0x65, 0x72, 0x12, 0x45, 0x0a, 0x08, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x69, 0x64, 0x18,\n\t0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x2a, 0x9a, 0x84, 0x9e, 0x03, 0x25, 0x67, 0x6f, 0x72, 0x6d,\n\t0x3a, 0x22, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x4b, 0x65, 0x79, 0x3b, 0x61, 0x75, 0x74,\n\t0x6f, 0x49, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65,\n\t0x22, 0x52, 0x07, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x72,\n\t0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09,\n\t0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x22, 0x78, 0x0a, 0x20, 0x52, 0x65, 0x70,\n\t0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52,\n\t0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x1a, 0x3b, 0x0a,\n\t0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x30, 0x0a, 0x05, 0x67, 0x72, 0x6f, 0x75,\n\t0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65,\n\t0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72,\n\t0x6f, 0x75, 0x70, 0x52, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x1a, 0x17, 0x0a, 0x05, 0x52, 0x65,\n\t0x70, 0x6c, 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x6f, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52,\n\t0x02, 0x6f, 0x6b, 0x22, 0xe1, 0x01, 0x0a, 0x14, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74,\n\t0x65, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x73, 0x1a, 0x09, 0x0a, 0x07,\n\t0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0xbd, 0x01, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c,\n\t0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18,\n\t0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x41, 0x74,\n\t0x12, 0x2b, 0x0a, 0x11, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x67,\n\t0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x72, 0x65, 0x70,\n\t0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x34, 0x0a,\n\t0x16, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f,\n\t0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x14, 0x74,\n\t0x6f, 0x74, 0x61, 0x6c, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72,\n\t0x69, 0x65, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x6d, 0x65, 0x73,\n\t0x73, 0x61, 0x67, 0x65, 0x5f, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01,\n\t0x28, 0x03, 0x52, 0x13, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,\n\t0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x22, 0x92, 0x01, 0x0a, 0x13, 0x52, 0x65, 0x70, 0x6c,\n\t0x69, 0x63, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x74, 0x61, 0x74, 0x73, 0x1a,\n\t0x33, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x67, 0x72,\n\t0x6f, 0x75, 0x70, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01,\n\t0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x75, 0x62, 0x6c, 0x69,\n\t0x63, 0x4b, 0x65, 0x79, 0x1a, 0x46, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x3d, 0x0a,\n\t0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x77,\n\t0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69,\n\t0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64,\n\t0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x32, 0xab, 0x03, 0x0a,\n\t0x12, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76,\n\t0x69, 0x63, 0x65, 0x12, 0x92, 0x01, 0x0a, 0x0e, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74,\n\t0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x40, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74,\n\t0x2e, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e,\n\t0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69,\n\t0x63, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70,\n\t0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3e, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e,\n\t0x65, 0x74, 0x2e, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76,\n\t0x31, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72,\n\t0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f,\n\t0x75, 0x70, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x80, 0x01, 0x0a, 0x14, 0x52, 0x65, 0x70,\n\t0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74,\n\t0x73, 0x12, 0x34, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x72, 0x65, 0x70, 0x6c,\n\t0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x69,\n\t0x63, 0x61, 0x74, 0x65, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x73, 0x2e,\n\t0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65,\n\t0x74, 0x2e, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31,\n\t0x2e, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c,\n\t0x53, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x7d, 0x0a, 0x13, 0x52,\n\t0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x74, 0x61,\n\t0x74, 0x73, 0x12, 0x33, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x72, 0x65, 0x70,\n\t0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6c,\n\t0x69, 0x63, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x74, 0x61, 0x74, 0x73, 0x2e,\n\t0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65,\n\t0x74, 0x2e, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31,\n\t0x2e, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53,\n\t0x74, 0x61, 0x74, 0x73, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x42, 0x2c, 0x5a, 0x2a, 0x62, 0x65,\n\t0x72, 0x74, 0x79, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x2f, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74,\n\t0x2f, 0x76, 0x32, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74,\n\t0x69, 0x6f, 0x6e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,\n}\n\nvar (\n\tfile_replicationtypes_bertyreplication_proto_rawDescOnce sync.Once\n\tfile_replicationtypes_bertyreplication_proto_rawDescData = file_replicationtypes_bertyreplication_proto_rawDesc\n)\n\nfunc file_replicationtypes_bertyreplication_proto_rawDescGZIP() []byte {\n\tfile_replicationtypes_bertyreplication_proto_rawDescOnce.Do(func() {\n\t\tfile_replicationtypes_bertyreplication_proto_rawDescData = protoimpl.X.CompressGZIP(file_replicationtypes_bertyreplication_proto_rawDescData)\n\t})\n\treturn file_replicationtypes_bertyreplication_proto_rawDescData\n}\n\nvar file_replicationtypes_bertyreplication_proto_msgTypes = make([]protoimpl.MessageInfo, 11)\nvar file_replicationtypes_bertyreplication_proto_goTypes = []any{\n\t(*ReplicatedGroup)(nil),                          // 0: weshnet.replication.v1.ReplicatedGroup\n\t(*ReplicatedGroupToken)(nil),                     // 1: weshnet.replication.v1.ReplicatedGroupToken\n\t(*ReplicationServiceReplicateGroup)(nil),         // 2: weshnet.replication.v1.ReplicationServiceReplicateGroup\n\t(*ReplicateGlobalStats)(nil),                     // 3: weshnet.replication.v1.ReplicateGlobalStats\n\t(*ReplicateGroupStats)(nil),                      // 4: weshnet.replication.v1.ReplicateGroupStats\n\t(*ReplicationServiceReplicateGroup_Request)(nil), // 5: weshnet.replication.v1.ReplicationServiceReplicateGroup.Request\n\t(*ReplicationServiceReplicateGroup_Reply)(nil),   // 6: weshnet.replication.v1.ReplicationServiceReplicateGroup.Reply\n\t(*ReplicateGlobalStats_Request)(nil),             // 7: weshnet.replication.v1.ReplicateGlobalStats.Request\n\t(*ReplicateGlobalStats_Reply)(nil),               // 8: weshnet.replication.v1.ReplicateGlobalStats.Reply\n\t(*ReplicateGroupStats_Request)(nil),              // 9: weshnet.replication.v1.ReplicateGroupStats.Request\n\t(*ReplicateGroupStats_Reply)(nil),                // 10: weshnet.replication.v1.ReplicateGroupStats.Reply\n\t(*protocoltypes.Group)(nil),                      // 11: weshnet.protocol.v1.Group\n}\nvar file_replicationtypes_bertyreplication_proto_depIdxs = []int32{\n\t0,  // 0: weshnet.replication.v1.ReplicatedGroupToken.replicated_group:type_name -> weshnet.replication.v1.ReplicatedGroup\n\t11, // 1: weshnet.replication.v1.ReplicationServiceReplicateGroup.Request.group:type_name -> weshnet.protocol.v1.Group\n\t0,  // 2: weshnet.replication.v1.ReplicateGroupStats.Reply.group:type_name -> weshnet.replication.v1.ReplicatedGroup\n\t5,  // 3: weshnet.replication.v1.ReplicationService.ReplicateGroup:input_type -> weshnet.replication.v1.ReplicationServiceReplicateGroup.Request\n\t7,  // 4: weshnet.replication.v1.ReplicationService.ReplicateGlobalStats:input_type -> weshnet.replication.v1.ReplicateGlobalStats.Request\n\t9,  // 5: weshnet.replication.v1.ReplicationService.ReplicateGroupStats:input_type -> weshnet.replication.v1.ReplicateGroupStats.Request\n\t6,  // 6: weshnet.replication.v1.ReplicationService.ReplicateGroup:output_type -> weshnet.replication.v1.ReplicationServiceReplicateGroup.Reply\n\t8,  // 7: weshnet.replication.v1.ReplicationService.ReplicateGlobalStats:output_type -> weshnet.replication.v1.ReplicateGlobalStats.Reply\n\t10, // 8: weshnet.replication.v1.ReplicationService.ReplicateGroupStats:output_type -> weshnet.replication.v1.ReplicateGroupStats.Reply\n\t6,  // [6:9] is the sub-list for method output_type\n\t3,  // [3:6] is the sub-list for method input_type\n\t3,  // [3:3] is the sub-list for extension type_name\n\t3,  // [3:3] is the sub-list for extension extendee\n\t0,  // [0:3] is the sub-list for field type_name\n}\n\nfunc init() { file_replicationtypes_bertyreplication_proto_init() }\nfunc file_replicationtypes_bertyreplication_proto_init() {\n\tif File_replicationtypes_bertyreplication_proto != nil {\n\t\treturn\n\t}\n\tif !protoimpl.UnsafeEnabled {\n\t\tfile_replicationtypes_bertyreplication_proto_msgTypes[0].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ReplicatedGroup); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_replicationtypes_bertyreplication_proto_msgTypes[1].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ReplicatedGroupToken); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_replicationtypes_bertyreplication_proto_msgTypes[2].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ReplicationServiceReplicateGroup); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_replicationtypes_bertyreplication_proto_msgTypes[3].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ReplicateGlobalStats); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_replicationtypes_bertyreplication_proto_msgTypes[4].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ReplicateGroupStats); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_replicationtypes_bertyreplication_proto_msgTypes[5].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ReplicationServiceReplicateGroup_Request); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_replicationtypes_bertyreplication_proto_msgTypes[6].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ReplicationServiceReplicateGroup_Reply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_replicationtypes_bertyreplication_proto_msgTypes[7].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ReplicateGlobalStats_Request); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_replicationtypes_bertyreplication_proto_msgTypes[8].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ReplicateGlobalStats_Reply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_replicationtypes_bertyreplication_proto_msgTypes[9].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ReplicateGroupStats_Request); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_replicationtypes_bertyreplication_proto_msgTypes[10].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*ReplicateGroupStats_Reply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n\ttype x struct{}\n\tout := protoimpl.TypeBuilder{\n\t\tFile: protoimpl.DescBuilder{\n\t\t\tGoPackagePath: reflect.TypeOf(x{}).PkgPath(),\n\t\t\tRawDescriptor: file_replicationtypes_bertyreplication_proto_rawDesc,\n\t\t\tNumEnums:      0,\n\t\t\tNumMessages:   11,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   1,\n\t\t},\n\t\tGoTypes:           file_replicationtypes_bertyreplication_proto_goTypes,\n\t\tDependencyIndexes: file_replicationtypes_bertyreplication_proto_depIdxs,\n\t\tMessageInfos:      file_replicationtypes_bertyreplication_proto_msgTypes,\n\t}.Build()\n\tFile_replicationtypes_bertyreplication_proto = out.File\n\tfile_replicationtypes_bertyreplication_proto_rawDesc = nil\n\tfile_replicationtypes_bertyreplication_proto_goTypes = nil\n\tfile_replicationtypes_bertyreplication_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "pkg/replicationtypes/bertyreplication.pb.gw.go",
    "content": "// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.\n// source: replicationtypes/bertyreplication.proto\n\n/*\nPackage replicationtypes is a reverse proxy.\n\nIt translates gRPC into RESTful JSON APIs.\n*/\npackage replicationtypes\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"net/http\"\n\n\t\"github.com/golang/protobuf/descriptor\"\n\t\"github.com/golang/protobuf/proto\"\n\t\"github.com/grpc-ecosystem/grpc-gateway/runtime\"\n\t\"github.com/grpc-ecosystem/grpc-gateway/utilities\"\n\t\"google.golang.org/grpc\"\n\t\"google.golang.org/grpc/codes\"\n\t\"google.golang.org/grpc/grpclog\"\n\t\"google.golang.org/grpc/metadata\"\n\t\"google.golang.org/grpc/status\"\n)\n\n// Suppress \"imported and not used\" errors\nvar _ codes.Code\nvar _ io.Reader\nvar _ status.Status\nvar _ = runtime.String\nvar _ = utilities.NewDoubleArray\nvar _ = descriptor.ForMessage\nvar _ = metadata.Join\n\nfunc request_ReplicationService_ReplicateGroup_0(ctx context.Context, marshaler runtime.Marshaler, client ReplicationServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq ReplicationServiceReplicateGroup_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := client.ReplicateGroup(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))\n\treturn msg, metadata, err\n\n}\n\nfunc local_request_ReplicationService_ReplicateGroup_0(ctx context.Context, marshaler runtime.Marshaler, server ReplicationServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq ReplicationServiceReplicateGroup_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := server.ReplicateGroup(ctx, &protoReq)\n\treturn msg, metadata, err\n\n}\n\nfunc request_ReplicationService_ReplicateGlobalStats_0(ctx context.Context, marshaler runtime.Marshaler, client ReplicationServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq ReplicateGlobalStats_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := client.ReplicateGlobalStats(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))\n\treturn msg, metadata, err\n\n}\n\nfunc local_request_ReplicationService_ReplicateGlobalStats_0(ctx context.Context, marshaler runtime.Marshaler, server ReplicationServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq ReplicateGlobalStats_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := server.ReplicateGlobalStats(ctx, &protoReq)\n\treturn msg, metadata, err\n\n}\n\nfunc request_ReplicationService_ReplicateGroupStats_0(ctx context.Context, marshaler runtime.Marshaler, client ReplicationServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq ReplicateGroupStats_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := client.ReplicateGroupStats(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))\n\treturn msg, metadata, err\n\n}\n\nfunc local_request_ReplicationService_ReplicateGroupStats_0(ctx context.Context, marshaler runtime.Marshaler, server ReplicationServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {\n\tvar protoReq ReplicateGroupStats_Request\n\tvar metadata runtime.ServerMetadata\n\n\tnewReader, berr := utilities.IOReaderFactory(req.Body)\n\tif berr != nil {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", berr)\n\t}\n\tif err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {\n\t\treturn nil, metadata, status.Errorf(codes.InvalidArgument, \"%v\", err)\n\t}\n\n\tmsg, err := server.ReplicateGroupStats(ctx, &protoReq)\n\treturn msg, metadata, err\n\n}\n\n// RegisterReplicationServiceHandlerServer registers the http handlers for service ReplicationService to \"mux\".\n// UnaryRPC     :call ReplicationServiceServer directly.\n// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.\n// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterReplicationServiceHandlerFromEndpoint instead.\nfunc RegisterReplicationServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server ReplicationServiceServer) error {\n\n\tmux.Handle(\"POST\", pattern_ReplicationService_ReplicateGroup_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tvar stream runtime.ServerTransportStream\n\t\tctx = grpc.NewContextWithServerTransportStream(ctx, &stream)\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := local_request_ReplicationService_ReplicateGroup_0(rctx, inboundMarshaler, server, req, pathParams)\n\t\tmd.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ReplicationService_ReplicateGroup_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ReplicationService_ReplicateGlobalStats_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tvar stream runtime.ServerTransportStream\n\t\tctx = grpc.NewContextWithServerTransportStream(ctx, &stream)\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := local_request_ReplicationService_ReplicateGlobalStats_0(rctx, inboundMarshaler, server, req, pathParams)\n\t\tmd.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ReplicationService_ReplicateGlobalStats_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ReplicationService_ReplicateGroupStats_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tvar stream runtime.ServerTransportStream\n\t\tctx = grpc.NewContextWithServerTransportStream(ctx, &stream)\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := local_request_ReplicationService_ReplicateGroupStats_0(rctx, inboundMarshaler, server, req, pathParams)\n\t\tmd.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ReplicationService_ReplicateGroupStats_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\treturn nil\n}\n\n// RegisterReplicationServiceHandlerFromEndpoint is same as RegisterReplicationServiceHandler but\n// automatically dials to \"endpoint\" and closes the connection when \"ctx\" gets done.\nfunc RegisterReplicationServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {\n\tconn, err := grpc.Dial(endpoint, opts...)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer func() {\n\t\tif err != nil {\n\t\t\tif cerr := conn.Close(); cerr != nil {\n\t\t\t\tgrpclog.Infof(\"Failed to close conn to %s: %v\", endpoint, cerr)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\tgo func() {\n\t\t\t<-ctx.Done()\n\t\t\tif cerr := conn.Close(); cerr != nil {\n\t\t\t\tgrpclog.Infof(\"Failed to close conn to %s: %v\", endpoint, cerr)\n\t\t\t}\n\t\t}()\n\t}()\n\n\treturn RegisterReplicationServiceHandler(ctx, mux, conn)\n}\n\n// RegisterReplicationServiceHandler registers the http handlers for service ReplicationService to \"mux\".\n// The handlers forward requests to the grpc endpoint over \"conn\".\nfunc RegisterReplicationServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {\n\treturn RegisterReplicationServiceHandlerClient(ctx, mux, NewReplicationServiceClient(conn))\n}\n\n// RegisterReplicationServiceHandlerClient registers the http handlers for service ReplicationService\n// to \"mux\". The handlers forward requests to the grpc endpoint over the given implementation of \"ReplicationServiceClient\".\n// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in \"ReplicationServiceClient\"\n// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in\n// \"ReplicationServiceClient\" to call the correct interceptors.\nfunc RegisterReplicationServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client ReplicationServiceClient) error {\n\n\tmux.Handle(\"POST\", pattern_ReplicationService_ReplicateGroup_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := request_ReplicationService_ReplicateGroup_0(rctx, inboundMarshaler, client, req, pathParams)\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ReplicationService_ReplicateGroup_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ReplicationService_ReplicateGlobalStats_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := request_ReplicationService_ReplicateGlobalStats_0(rctx, inboundMarshaler, client, req, pathParams)\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ReplicationService_ReplicateGlobalStats_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\tmux.Handle(\"POST\", pattern_ReplicationService_ReplicateGroupStats_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {\n\t\tctx, cancel := context.WithCancel(req.Context())\n\t\tdefer cancel()\n\t\tinboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)\n\t\trctx, err := runtime.AnnotateContext(ctx, mux, req)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\t\tresp, md, err := request_ReplicationService_ReplicateGroupStats_0(rctx, inboundMarshaler, client, req, pathParams)\n\t\tctx = runtime.NewServerMetadataContext(ctx, md)\n\t\tif err != nil {\n\t\t\truntime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)\n\t\t\treturn\n\t\t}\n\n\t\tforward_ReplicationService_ReplicateGroupStats_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)\n\n\t})\n\n\treturn nil\n}\n\nvar (\n\tpattern_ReplicationService_ReplicateGroup_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{\"weshnet.replication.v1.ReplicationService\", \"ReplicateGroup\"}, \"\", runtime.AssumeColonVerbOpt(true)))\n\n\tpattern_ReplicationService_ReplicateGlobalStats_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{\"weshnet.replication.v1.ReplicationService\", \"ReplicateGlobalStats\"}, \"\", runtime.AssumeColonVerbOpt(true)))\n\n\tpattern_ReplicationService_ReplicateGroupStats_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{\"weshnet.replication.v1.ReplicationService\", \"ReplicateGroupStats\"}, \"\", runtime.AssumeColonVerbOpt(true)))\n)\n\nvar (\n\tforward_ReplicationService_ReplicateGroup_0 = runtime.ForwardResponseMessage\n\n\tforward_ReplicationService_ReplicateGlobalStats_0 = runtime.ForwardResponseMessage\n\n\tforward_ReplicationService_ReplicateGroupStats_0 = runtime.ForwardResponseMessage\n)\n"
  },
  {
    "path": "pkg/replicationtypes/bertyreplication_grpc.pb.go",
    "content": "// Code generated by protoc-gen-go-grpc. DO NOT EDIT.\n// versions:\n// - protoc-gen-go-grpc v1.5.1\n// - protoc             (unknown)\n// source: replicationtypes/bertyreplication.proto\n\npackage replicationtypes\n\nimport (\n\tcontext \"context\"\n\tgrpc \"google.golang.org/grpc\"\n\tcodes \"google.golang.org/grpc/codes\"\n\tstatus \"google.golang.org/grpc/status\"\n)\n\n// This is a compile-time assertion to ensure that this generated file\n// is compatible with the grpc package it is being compiled against.\n// Requires gRPC-Go v1.64.0 or later.\nconst _ = grpc.SupportPackageIsVersion9\n\nconst (\n\tReplicationService_ReplicateGroup_FullMethodName       = \"/weshnet.replication.v1.ReplicationService/ReplicateGroup\"\n\tReplicationService_ReplicateGlobalStats_FullMethodName = \"/weshnet.replication.v1.ReplicationService/ReplicateGlobalStats\"\n\tReplicationService_ReplicateGroupStats_FullMethodName  = \"/weshnet.replication.v1.ReplicationService/ReplicateGroupStats\"\n)\n\n// ReplicationServiceClient is the client API for ReplicationService service.\n//\n// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.\n//\n// ReplicationService\ntype ReplicationServiceClient interface {\n\t// ReplicateGroup\n\tReplicateGroup(ctx context.Context, in *ReplicationServiceReplicateGroup_Request, opts ...grpc.CallOption) (*ReplicationServiceReplicateGroup_Reply, error)\n\tReplicateGlobalStats(ctx context.Context, in *ReplicateGlobalStats_Request, opts ...grpc.CallOption) (*ReplicateGlobalStats_Reply, error)\n\tReplicateGroupStats(ctx context.Context, in *ReplicateGroupStats_Request, opts ...grpc.CallOption) (*ReplicateGroupStats_Reply, error)\n}\n\ntype replicationServiceClient struct {\n\tcc grpc.ClientConnInterface\n}\n\nfunc NewReplicationServiceClient(cc grpc.ClientConnInterface) ReplicationServiceClient {\n\treturn &replicationServiceClient{cc}\n}\n\nfunc (c *replicationServiceClient) ReplicateGroup(ctx context.Context, in *ReplicationServiceReplicateGroup_Request, opts ...grpc.CallOption) (*ReplicationServiceReplicateGroup_Reply, error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tout := new(ReplicationServiceReplicateGroup_Reply)\n\terr := c.cc.Invoke(ctx, ReplicationService_ReplicateGroup_FullMethodName, in, out, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *replicationServiceClient) ReplicateGlobalStats(ctx context.Context, in *ReplicateGlobalStats_Request, opts ...grpc.CallOption) (*ReplicateGlobalStats_Reply, error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tout := new(ReplicateGlobalStats_Reply)\n\terr := c.cc.Invoke(ctx, ReplicationService_ReplicateGlobalStats_FullMethodName, in, out, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *replicationServiceClient) ReplicateGroupStats(ctx context.Context, in *ReplicateGroupStats_Request, opts ...grpc.CallOption) (*ReplicateGroupStats_Reply, error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tout := new(ReplicateGroupStats_Reply)\n\terr := c.cc.Invoke(ctx, ReplicationService_ReplicateGroupStats_FullMethodName, in, out, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\n// ReplicationServiceServer is the server API for ReplicationService service.\n// All implementations must embed UnimplementedReplicationServiceServer\n// for forward compatibility.\n//\n// ReplicationService\ntype ReplicationServiceServer interface {\n\t// ReplicateGroup\n\tReplicateGroup(context.Context, *ReplicationServiceReplicateGroup_Request) (*ReplicationServiceReplicateGroup_Reply, error)\n\tReplicateGlobalStats(context.Context, *ReplicateGlobalStats_Request) (*ReplicateGlobalStats_Reply, error)\n\tReplicateGroupStats(context.Context, *ReplicateGroupStats_Request) (*ReplicateGroupStats_Reply, error)\n\tmustEmbedUnimplementedReplicationServiceServer()\n}\n\n// UnimplementedReplicationServiceServer must be embedded to have\n// forward compatible implementations.\n//\n// NOTE: this should be embedded by value instead of pointer to avoid a nil\n// pointer dereference when methods are called.\ntype UnimplementedReplicationServiceServer struct{}\n\nfunc (UnimplementedReplicationServiceServer) ReplicateGroup(context.Context, *ReplicationServiceReplicateGroup_Request) (*ReplicationServiceReplicateGroup_Reply, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method ReplicateGroup not implemented\")\n}\nfunc (UnimplementedReplicationServiceServer) ReplicateGlobalStats(context.Context, *ReplicateGlobalStats_Request) (*ReplicateGlobalStats_Reply, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method ReplicateGlobalStats not implemented\")\n}\nfunc (UnimplementedReplicationServiceServer) ReplicateGroupStats(context.Context, *ReplicateGroupStats_Request) (*ReplicateGroupStats_Reply, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method ReplicateGroupStats not implemented\")\n}\nfunc (UnimplementedReplicationServiceServer) mustEmbedUnimplementedReplicationServiceServer() {}\nfunc (UnimplementedReplicationServiceServer) testEmbeddedByValue()                            {}\n\n// UnsafeReplicationServiceServer may be embedded to opt out of forward compatibility for this service.\n// Use of this interface is not recommended, as added methods to ReplicationServiceServer will\n// result in compilation errors.\ntype UnsafeReplicationServiceServer interface {\n\tmustEmbedUnimplementedReplicationServiceServer()\n}\n\nfunc RegisterReplicationServiceServer(s grpc.ServiceRegistrar, srv ReplicationServiceServer) {\n\t// If the following call pancis, it indicates UnimplementedReplicationServiceServer was\n\t// embedded by pointer and is nil.  This will cause panics if an\n\t// unimplemented method is ever invoked, so we test this at initialization\n\t// time to prevent it from happening at runtime later due to I/O.\n\tif t, ok := srv.(interface{ testEmbeddedByValue() }); ok {\n\t\tt.testEmbeddedByValue()\n\t}\n\ts.RegisterService(&ReplicationService_ServiceDesc, srv)\n}\n\nfunc _ReplicationService_ReplicateGroup_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ReplicationServiceReplicateGroup_Request)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(ReplicationServiceServer).ReplicateGroup(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: ReplicationService_ReplicateGroup_FullMethodName,\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(ReplicationServiceServer).ReplicateGroup(ctx, req.(*ReplicationServiceReplicateGroup_Request))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _ReplicationService_ReplicateGlobalStats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ReplicateGlobalStats_Request)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(ReplicationServiceServer).ReplicateGlobalStats(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: ReplicationService_ReplicateGlobalStats_FullMethodName,\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(ReplicationServiceServer).ReplicateGlobalStats(ctx, req.(*ReplicateGlobalStats_Request))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _ReplicationService_ReplicateGroupStats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ReplicateGroupStats_Request)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(ReplicationServiceServer).ReplicateGroupStats(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: ReplicationService_ReplicateGroupStats_FullMethodName,\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(ReplicationServiceServer).ReplicateGroupStats(ctx, req.(*ReplicateGroupStats_Request))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\n// ReplicationService_ServiceDesc is the grpc.ServiceDesc for ReplicationService service.\n// It's only intended for direct use with grpc.RegisterService,\n// and not to be introspected or modified (even as a copy)\nvar ReplicationService_ServiceDesc = grpc.ServiceDesc{\n\tServiceName: \"weshnet.replication.v1.ReplicationService\",\n\tHandlerType: (*ReplicationServiceServer)(nil),\n\tMethods: []grpc.MethodDesc{\n\t\t{\n\t\t\tMethodName: \"ReplicateGroup\",\n\t\t\tHandler:    _ReplicationService_ReplicateGroup_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"ReplicateGlobalStats\",\n\t\t\tHandler:    _ReplicationService_ReplicateGlobalStats_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"ReplicateGroupStats\",\n\t\t\tHandler:    _ReplicationService_ReplicateGroupStats_Handler,\n\t\t},\n\t},\n\tStreams:  []grpc.StreamDesc{},\n\tMetadata: \"replicationtypes/bertyreplication.proto\",\n}\n"
  },
  {
    "path": "pkg/replicationtypes/consts.go",
    "content": "package replicationtypes\n\nconst (\n\tServiceReplicationRegisteredPrefix = \"user_registration\"\n\tServiceReplicationKeyGroupPrefix   = \"group\"\n)\n"
  },
  {
    "path": "pkg/replicationtypes/models.go",
    "content": "package replicationtypes\n\nimport (\n\t\"encoding/base64\"\n\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n)\n\nfunc (m *ReplicatedGroup) ToGroup() (*protocoltypes.Group, error) {\n\tpk, err := base64.RawURLEncoding.DecodeString(m.PublicKey)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tsignPub, err := base64.RawURLEncoding.DecodeString(m.SignPub)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tlinkKey, err := base64.RawURLEncoding.DecodeString(m.LinkKey)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &protocoltypes.Group{\n\t\tPublicKey: pk,\n\t\tSignPub:   signPub,\n\t\tLinkKey:   linkKey,\n\t}, nil\n}\n"
  },
  {
    "path": "pkg/secretstore/chain_key.go",
    "content": "package secretstore\n\nimport (\n\tcrand \"crypto/rand\"\n\t\"fmt\"\n\n\t\"github.com/libp2p/go-libp2p/core/crypto\"\n\t\"golang.org/x/crypto/nacl/box\"\n\t\"google.golang.org/protobuf/proto\"\n\n\t\"berty.tech/weshnet/v2/pkg/cryptoutil\"\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n)\n\n// newDeviceChainKey creates a new random chain key\nfunc newDeviceChainKey() (*protocoltypes.DeviceChainKey, error) {\n\tchainKey := make([]byte, 32)\n\t_, err := crand.Read(chainKey)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrCryptoRandomGeneration.Wrap(err)\n\t}\n\n\treturn &protocoltypes.DeviceChainKey{\n\t\tChainKey: chainKey,\n\t\tCounter:  0,\n\t}, nil\n}\n\n// encryptDeviceChainKey encrypts a device chain key for a target member\nfunc encryptDeviceChainKey(localDevicePrivateKey crypto.PrivKey, remoteMemberPubKey crypto.PubKey, deviceChainKey *protocoltypes.DeviceChainKey, group *protocoltypes.Group) ([]byte, error) {\n\tchainKeyBytes, err := proto.Marshal(deviceChainKey)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\tmongPriv, mongPub, err := cryptoutil.EdwardsToMontgomery(localDevicePrivateKey, remoteMemberPubKey)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrCryptoKeyConversion.Wrap(err)\n\t}\n\n\tnonce := groupIDToNonce(group)\n\tencryptedChainKey := box.Seal(nil, chainKeyBytes, nonce, mongPub, mongPriv)\n\n\treturn encryptedChainKey, nil\n}\n\n// decryptDeviceChainKey decrypts a chain key sent by the given device\nfunc decryptDeviceChainKey(encryptedDeviceChainKey []byte, group *protocoltypes.Group, localMemberPrivateKey crypto.PrivKey, senderDevicePubKey crypto.PubKey) (*protocoltypes.DeviceChainKey, error) {\n\tmongPriv, mongPub, err := cryptoutil.EdwardsToMontgomery(localMemberPrivateKey, senderDevicePubKey)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrCryptoKeyConversion.Wrap(err)\n\t}\n\n\tnonce := groupIDToNonce(group)\n\tdecryptedSecret := &protocoltypes.DeviceChainKey{}\n\tdecryptedMessage, ok := box.Open(nil, encryptedDeviceChainKey, nonce, mongPub, mongPriv)\n\tif !ok {\n\t\treturn nil, errcode.ErrCode_ErrCryptoDecrypt.Wrap(fmt.Errorf(\"unable to decrypt message\"))\n\t}\n\n\terr = proto.Unmarshal(decryptedMessage, decryptedSecret)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\treturn decryptedSecret, nil\n}\n\n// groupIDToNonce converts a group public key to a value which can be used as\n// a nonce of the nacl library\nfunc groupIDToNonce(group *protocoltypes.Group) *[cryptoutil.NonceSize]byte {\n\t// Nonce doesn't need to be secret, random nor unpredictable, it just needs\n\t// to be used only once for a given sender+receiver set, and we will send\n\t// only one SecretEntryPayload per localDevicePrivateKey+remoteMemberPubKey\n\t// So we can reuse groupID as nonce for all SecretEntryPayload and save\n\t// 24 bytes of storage and bandwidth for each of them.\n\t//\n\t// See https://pynacl.readthedocs.io/en/stable/secret/#nonce\n\t// See Security Model here: https://nacl.cr.yp.to/box.html\n\tvar nonce [cryptoutil.NonceSize]byte\n\n\tgid := group.GetPublicKey()\n\n\tcopy(nonce[:], gid)\n\n\treturn &nonce\n}\n"
  },
  {
    "path": "pkg/secretstore/datastore_keys.go",
    "content": "package secretstore\n\nimport (\n\t\"encoding/base64\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\n\t\"github.com/ipfs/go-cid\"\n\t\"github.com/ipfs/go-datastore\"\n\t\"github.com/libp2p/go-libp2p/core/crypto\"\n\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n)\n\nconst (\n\t// dsNamespaceChainKeyForDeviceOnGroup is a namespace stores the current\n\t// state of a device chain key for a given group.\n\t// It contains the secret used to derive the next value of the chain key\n\t// and used to generate a message key for the message at `counter` value,\n\t// then put in the dsNamespacePrecomputedMessageKeys namespace.\n\tdsNamespaceChainKeyForDeviceOnGroup = \"chainKeyForDeviceOnGroup\"\n\n\t// dsNamespacePrecomputedMessageKeys is a namespace storing precomputed\n\t// message keys for a given group, device and message counter.\n\t// As the chain key stored has already been derived, these message keys\n\t// need to be computed beforehand.\n\t// The corresponding message can then be decrypted via a quick lookup.\n\tdsNamespacePrecomputedMessageKeys = \"precomputedMessageKeys\"\n\n\t// dsNamespaceMessageKeyForCIDs is a namespace containing the message key\n\t// for a given CID once the corresponding message has been decrypted.\n\tdsNamespaceMessageKeyForCIDs = \"messageKeyForCIDs\"\n\n\t// dsNamespaceOutOfStoreGroupHint is a namespace where HMAC value are\n\t// associated to a group public key.\n\t// It is used when receiving an out-of-store message (e.g. a push\n\t// notification) to identify the group on which the message belongs, which\n\t// can then be decrypted.\n\tdsNamespaceOutOfStoreGroupHint = \"outOfStoreGroupHint\"\n\n\t// dsNamespaceOutOfStoreGroupHintCounters is a namespace storing first and\n\t// last counter values for generated group hints inside the\n\t// dsNamespaceOutOfStoreGroupHint namespace\n\tdsNamespaceOutOfStoreGroupHintCounters = \"outOfStoreGroupHintCounters\"\n\n\t// dsNamespaceGroupDatastore is a namespace to store groups by their public\n\t// key\n\tdsNamespaceGroupDatastore = \"groupByPublicKey\"\n)\n\nfunc dsKeyForGroup(key []byte) datastore.Key {\n\treturn datastore.KeyWithNamespaces([]string{\n\t\tdsNamespaceGroupDatastore,\n\t\tbase64.RawURLEncoding.EncodeToString(key),\n\t})\n}\n\n// dsKeyForPrecomputedMessageKey returns a datastore.Key where will be stored a\n// precalculated message key for a given group and device\nfunc dsKeyForPrecomputedMessageKey(groupPublicKey, devicePublicKey []byte, counter uint64) datastore.Key {\n\treturn datastore.KeyWithNamespaces([]string{\n\t\tdsNamespacePrecomputedMessageKeys,\n\t\thex.EncodeToString(groupPublicKey),\n\t\thex.EncodeToString(devicePublicKey),\n\t\tfmt.Sprintf(\"%d\", counter),\n\t})\n}\n\n// dsKeyForCurrentChainKey returns a datastore.Key where will be stored a\n// device chain key for a given group.\nfunc dsKeyForCurrentChainKey(groupPublicKey crypto.PubKey, devicePublicKey crypto.PubKey) (datastore.Key, error) {\n\tdevicePublicKeyBytes, err := devicePublicKey.Raw()\n\tif err != nil {\n\t\treturn datastore.Key{}, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\tgroupPublicKeyBytes, err := groupPublicKey.Raw()\n\tif err != nil {\n\t\treturn datastore.Key{}, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\treturn datastore.KeyWithNamespaces([]string{\n\t\tdsNamespaceChainKeyForDeviceOnGroup,\n\t\thex.EncodeToString(groupPublicKeyBytes),\n\t\thex.EncodeToString(devicePublicKeyBytes),\n\t}), nil\n}\n\n// dsKeyForMessageKeyByCID returns a datastore.Key where will be stored a\n// message decryption key for a given message CID.\nfunc dsKeyForMessageKeyByCID(id cid.Cid) datastore.Key {\n\t// TODO: specify the id\n\treturn datastore.KeyWithNamespaces([]string{\n\t\tdsNamespaceMessageKeyForCIDs,\n\t\tid.String(),\n\t})\n}\n\n// dsKeyForOutOfStoreMessageGroupHint returns a datastore.Key where will be\n// stored a group public key for a given push group reference.\nfunc dsKeyForOutOfStoreMessageGroupHint(ref []byte) datastore.Key {\n\treturn datastore.KeyWithNamespaces([]string{\n\t\tdsNamespaceOutOfStoreGroupHint,\n\t\tbase64.RawURLEncoding.EncodeToString(ref),\n\t})\n}\n\n// dsKeyForOutOfStoreFirstLastCounters returns the datastore.Key where will be\n// stored a protocoltypes.FirstLastCounters struct for the given group public\n// key and device public key.\nfunc dsKeyForOutOfStoreFirstLastCounters(groupPK, devicePK []byte) datastore.Key {\n\treturn datastore.KeyWithNamespaces([]string{\n\t\tdsNamespaceOutOfStoreGroupHintCounters,\n\t\tbase64.RawURLEncoding.EncodeToString(groupPK),\n\t\tbase64.RawURLEncoding.EncodeToString(devicePK),\n\t})\n}\n"
  },
  {
    "path": "pkg/secretstore/device_keystore_wrapper.go",
    "content": "package secretstore\n\nimport (\n\t\"crypto/ed25519\"\n\tcrand \"crypto/rand\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/aead/ecdh\"\n\tkeystore \"github.com/ipfs/go-ipfs-keystore\"\n\t\"github.com/libp2p/go-libp2p/core/crypto\"\n\t\"go.uber.org/zap\"\n\n\t\"berty.tech/weshnet/v2/pkg/cryptoutil\"\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n)\n\nconst (\n\tkeyAccount      = \"accountSK\"\n\tkeyAccountProof = \"accountProofSK\"\n\tkeyDevice       = \"deviceSK\"\n\tkeyMemberDevice = \"memberDeviceSK\"\n\tkeyMember       = \"memberSK\"\n\tkeyContactGroup = \"contactGroupSK\"\n)\n\n// deviceKeystore is a wrapper around a keystore.Keystore object.\n// It contains methods to manipulate member and device keys.\ntype deviceKeystore struct {\n\tkeystore keystore.Keystore\n\tmu       sync.Mutex\n\tlogger   *zap.Logger\n}\n\n// newDeviceKeystore instantiate a new device keystore\nfunc newDeviceKeystore(ks keystore.Keystore, logger *zap.Logger) *deviceKeystore {\n\tif logger == nil {\n\t\tlogger = zap.NewNop()\n\t}\n\n\treturn &deviceKeystore{\n\t\tkeystore: ks,\n\t\tlogger:   logger,\n\t}\n}\n\n// getAccountPrivateKey returns the private key of the current account\nfunc (a *deviceKeystore) getAccountPrivateKey() (crypto.PrivKey, error) {\n\ta.mu.Lock()\n\tdefer a.mu.Unlock()\n\n\treturn a.getOrGenerateNamedKey(keyAccount)\n}\n\n// getAccountProofPrivateKey returns the private proof key of\n// the current account\nfunc (a *deviceKeystore) getAccountProofPrivateKey() (crypto.PrivKey, error) {\n\ta.mu.Lock()\n\tdefer a.mu.Unlock()\n\n\treturn a.getOrGenerateNamedKey(keyAccountProof)\n}\n\n// devicePrivateKey returns the current private key of the current device for\n// the account and one-to-one conversations\nfunc (a *deviceKeystore) devicePrivateKey() (crypto.PrivKey, error) {\n\ta.mu.Lock()\n\tdefer a.mu.Unlock()\n\n\treturn a.getOrGenerateNamedKey(keyDevice)\n}\n\n// contactGroupPrivateKey retrieves the key for the contact group\n// shared with the supplied contact's public key, this key will be derived to\n// form the contact group keys\nfunc (a *deviceKeystore) contactGroupPrivateKey(contactPublicKey crypto.PubKey) (crypto.PrivKey, error) {\n\taccountPrivateKey, err := a.getAccountPrivateKey()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\n\treturn a.getOrComputeECDH(keyContactGroup, contactPublicKey, accountPrivateKey)\n}\n\n// memberDeviceForMultiMemberGroup retrieves the device private key for the\n// supplied group\nfunc (a *deviceKeystore) memberDeviceForMultiMemberGroup(groupPublicKey crypto.PubKey) (*ownMemberDevice, error) {\n\tmemberPrivateKey, err := a.computeMemberKeyForMultiMemberGroup(groupPublicKey)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInternal.Wrap(fmt.Errorf(\"unable to get or generate a device key for group member: %w\", err))\n\t}\n\n\tdevicePrivateKey, err := a.getOrGenerateDeviceKeyForMultiMemberGroup(groupPublicKey)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\n\treturn newOwnMemberDevice(memberPrivateKey, devicePrivateKey), nil\n}\n\n// memberDeviceForGroup computes or retrieves the member and device key for the\n// supplied group\nfunc (a *deviceKeystore) memberDeviceForGroup(group *protocoltypes.Group) (*ownMemberDevice, error) {\n\tpublicKey, err := group.GetPubKey()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"unable to get public key for group: %w\", err))\n\t}\n\n\tswitch group.GetGroupType() {\n\tcase protocoltypes.GroupType_GroupTypeAccount, protocoltypes.GroupType_GroupTypeContact:\n\t\tmemberPrivateKey, err := a.getAccountPrivateKey()\n\t\tif err != nil {\n\t\t\treturn nil, errcode.ErrCode_ErrInternal.Wrap(err)\n\t\t}\n\n\t\tdevicePrivateKey, err := a.devicePrivateKey()\n\t\tif err != nil {\n\t\t\treturn nil, errcode.ErrCode_ErrInternal.Wrap(err)\n\t\t}\n\n\t\treturn newOwnMemberDevice(memberPrivateKey, devicePrivateKey), nil\n\n\tcase protocoltypes.GroupType_GroupTypeMultiMember:\n\t\treturn a.memberDeviceForMultiMemberGroup(publicKey)\n\t}\n\n\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"unknown group type\"))\n}\n\n// getOrGenerateNamedKey retrieves a private key by its name, or generate it\n// if missing\nfunc (a *deviceKeystore) getOrGenerateNamedKey(name string) (crypto.PrivKey, error) {\n\tprivateKey, err := a.keystore.Get(name)\n\tif err == nil {\n\t\treturn privateKey, nil\n\t} else if err.Error() != keystore.ErrNoSuchKey.Error() {\n\t\treturn nil, errcode.ErrCode_ErrDBRead.Wrap(fmt.Errorf(\"unable to perform get operation on keystore: %w\", err))\n\t}\n\n\tprivateKey, _, err = crypto.GenerateEd25519Key(crand.Reader)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(fmt.Errorf(\"unable to generate an ed25519 key: %w\", err))\n\t}\n\n\tif err := a.keystore.Put(name, privateKey); err != nil {\n\t\treturn nil, errcode.ErrCode_ErrDBWrite.Wrap(fmt.Errorf(\"unable to perform put operation on keystore: %w\", err))\n\t}\n\n\treturn privateKey, nil\n}\n\n// getOrGenerateDeviceKeyForMultiMemberGroup fetches or generate a new device\n// key for a multi-member group. The results do not need to be deterministic\n// as it will only be used on the current device.\nfunc (a *deviceKeystore) getOrGenerateDeviceKeyForMultiMemberGroup(groupPublicKey crypto.PubKey) (crypto.PrivKey, error) {\n\ta.mu.Lock()\n\tdefer a.mu.Unlock()\n\n\tgroupPublicKeyRaw, err := groupPublicKey.Raw()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\tname := strings.Join([]string{keyMemberDevice, hex.EncodeToString(groupPublicKeyRaw)}, \"_\")\n\n\treturn a.getOrGenerateNamedKey(name)\n}\n\n// getOrComputeECDH fetches a named private key or computes one via an\n// elliptic-curve Diffie-Hellman key agreement if not cached\nfunc (a *deviceKeystore) getOrComputeECDH(nameSpace string, publicKey crypto.PubKey, ownPrivateKey crypto.PrivKey) (crypto.PrivKey, error) {\n\ta.mu.Lock()\n\tdefer a.mu.Unlock()\n\n\tpublicKeyRaw, err := publicKey.Raw()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\tname := strings.Join([]string{nameSpace, hex.EncodeToString(publicKeyRaw)}, \"_\")\n\n\tprivateKey, err := a.keystore.Get(name)\n\tif err == nil {\n\t\treturn privateKey, nil\n\t} else if err.Error() != keystore.ErrNoSuchKey.Error() {\n\t\treturn nil, errcode.ErrCode_ErrDBRead.Wrap(fmt.Errorf(\"unable to perform get operation on keystore: %w\", err))\n\t}\n\n\tprivateKeyBytes, publicKeyBytes, err := cryptoutil.EdwardsToMontgomery(ownPrivateKey, publicKey)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrCryptoKeyConversion.Wrap(err)\n\t}\n\n\tsecret := ecdh.X25519().ComputeSecret(privateKeyBytes, publicKeyBytes)\n\tgroupSecretPrivateKey := ed25519.NewKeyFromSeed(secret)\n\n\tprivateKey, _, err = crypto.KeyPairFromStdKey(&groupSecretPrivateKey)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrCryptoKeyConversion.Wrap(err)\n\t}\n\n\tif err := a.keystore.Put(name, privateKey); err != nil {\n\t\treturn nil, errcode.ErrCode_ErrDBWrite.Wrap(err)\n\t}\n\n\treturn privateKey, nil\n}\n\n// computeMemberKeyForMultiMemberGroup returns a deterministic private key\n// for a multi member group, this allows a group to be joined from two\n// different devices simultaneously without requiring a consensus.\nfunc (a *deviceKeystore) computeMemberKeyForMultiMemberGroup(groupPublicKey crypto.PubKey) (crypto.PrivKey, error) {\n\taccountProofPrivateKey, err := a.getAccountProofPrivateKey()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\n\treturn a.getOrComputeECDH(keyMember, groupPublicKey, accountProofPrivateKey)\n}\n\n// restoreAccountKeys restores exported LibP2P keys into the deviceKeystore, it\n// will fail if accounts keys are already created or imported into the keystore\nfunc (a *deviceKeystore) restoreAccountKeys(accountPrivateKeyBytes []byte, accountProofPrivateKeyBytes []byte) error {\n\tprivateKeys := map[string]crypto.PrivKey{}\n\n\tfor keyName, keyBytes := range map[string][]byte{\n\t\tkeyAccount:      accountPrivateKeyBytes,\n\t\tkeyAccountProof: accountProofPrivateKeyBytes,\n\t} {\n\t\tvar err error\n\t\tprivateKeys[keyName], err = getEd25519PrivateKeyFromLibP2PFormattedBytes(keyBytes)\n\t\tif err != nil {\n\t\t\treturn errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t\t}\n\t}\n\n\tif privateKeys[keyAccount].Equals(privateKeys[keyAccountProof]) {\n\t\treturn errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"the account key cannot be the same value as the account proof key\"))\n\t}\n\n\tfor keyName := range privateKeys {\n\t\tif exists, err := a.keystore.Has(keyName); err != nil {\n\t\t\treturn errcode.ErrCode_ErrDBRead.Wrap(err)\n\t\t} else if exists {\n\t\t\treturn errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"an account is already set in this keystore\"))\n\t\t}\n\t}\n\n\tfor keyName, privateKey := range privateKeys {\n\t\tif err := a.keystore.Put(keyName, privateKey); err != nil {\n\t\t\treturn errcode.ErrCode_ErrDBWrite.Wrap(err)\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/secretstore/device_keystore_wrapper_test.go",
    "content": "package secretstore_test\n\nimport (\n\tcrand \"crypto/rand\"\n\t\"testing\"\n\n\t\"github.com/libp2p/go-libp2p/core/crypto\"\n\t\"github.com/stretchr/testify/assert\"\n\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n\t\"berty.tech/weshnet/v2/pkg/secretstore\"\n)\n\nfunc Test_New_AccountPrivKey_AccountProofPrivKey(t *testing.T) {\n\tacc, err := secretstore.NewInMemSecretStore(nil)\n\tassert.NoError(t, err)\n\tassert.NotNil(t, acc)\n\n\tsk1, skProof1, err := acc.ExportAccountKeysForBackup()\n\tassert.NoError(t, err)\n\tassert.NotNil(t, sk1)\n\tassert.NotNil(t, skProof1)\n\n\tsk2, skProof2, err := acc.ExportAccountKeysForBackup()\n\tassert.NoError(t, err)\n\tassert.NotNil(t, sk2)\n\tassert.NotNil(t, skProof2)\n\n\tassert.Equal(t, sk1, sk2)\n\tassert.Equal(t, skProof1, skProof2)\n\n\tassert.NotEqual(t, sk1, skProof1)\n\tassert.NotEqual(t, sk1, skProof2)\n\tassert.NotEqual(t, sk2, skProof1)\n\tassert.NotEqual(t, sk2, skProof2)\n}\n\nfunc Test_ExportAccountKeys_ImportAccountKeys(t *testing.T) {\n\tacc1, err := secretstore.NewInMemSecretStore(nil)\n\tassert.NoError(t, err)\n\tassert.NotNil(t, acc1)\n\n\tsk1, skProof1, err := acc1.ExportAccountKeysForBackup()\n\tassert.NoError(t, err)\n\tassert.NotNil(t, sk1)\n\tassert.NotNil(t, skProof1)\n\n\tacc2, err := secretstore.NewInMemSecretStore(nil)\n\tassert.NoError(t, err)\n\tassert.NotNil(t, acc2)\n\n\t// Testing with a nil value\n\t{\n\t\tassert.Error(t, acc2.ImportAccountKeys(nil, skProof1))\n\t\tassert.Error(t, acc2.ImportAccountKeys(sk1, nil))\n\t}\n\n\t// Testing with an unsupported key format\n\t{\n\t\tinvalidPriv, _, err := crypto.GenerateSecp256k1Key(crand.Reader)\n\t\tassert.NoError(t, err)\n\n\t\tinvalidPrivBytes, err := crypto.MarshalPrivateKey(invalidPriv)\n\t\tassert.NoError(t, err)\n\n\t\tassert.Error(t, acc2.ImportAccountKeys(sk1, invalidPrivBytes))\n\t\tassert.Error(t, acc2.ImportAccountKeys(invalidPrivBytes, skProof1))\n\t}\n\n\t// Testing with an invalid key format\n\t{\n\t\tgarbageBytes := []byte(\"garbage\")\n\t\tassert.Error(t, acc2.ImportAccountKeys(sk1, garbageBytes))\n\t\tassert.Error(t, acc2.ImportAccountKeys(garbageBytes, skProof1))\n\t}\n\n\t// Testing with account and proof key being the same\n\t{\n\t\tassert.Error(t, acc2.ImportAccountKeys(sk1, sk1))\n\t}\n\n\t// Valid test case\n\t{\n\t\tassert.NoError(t, acc2.ImportAccountKeys(sk1, skProof1))\n\t}\n\n\t// Attempting to import keys again\n\t{\n\t\tassert.Error(t, acc2.ImportAccountKeys(sk1, skProof1))\n\t}\n\n\tsk2, skProof2, err := acc1.ExportAccountKeysForBackup()\n\tassert.NoError(t, err)\n\tassert.NotNil(t, sk2)\n\tassert.NotNil(t, skProof2)\n\n\tassert.Equal(t, sk1, sk2)\n\tassert.Equal(t, skProof1, skProof2)\n\tassert.NotEqual(t, sk1, skProof1)\n\tassert.NotEqual(t, sk1, skProof2)\n\tassert.NotEqual(t, sk2, skProof1)\n\tassert.NotEqual(t, sk2, skProof2)\n}\n\nfunc Test_DevicePrivKey(t *testing.T) {\n\tacc1, err := secretstore.NewInMemSecretStore(nil)\n\tassert.NoError(t, err)\n\tassert.NotNil(t, acc1)\n\n\tsk1, skProof1, err := acc1.ExportAccountKeysForBackup()\n\tassert.NoError(t, err)\n\tassert.NotNil(t, sk1)\n\tassert.NotNil(t, skProof1)\n\n\tacc2, err := secretstore.NewInMemSecretStore(nil)\n\tassert.NoError(t, err)\n\tassert.NotNil(t, acc1)\n\n\terr = acc2.ImportAccountKeys(sk1, skProof1)\n\tassert.NoError(t, err)\n\n\tsk2, skProof2, err := acc2.ExportAccountKeysForBackup()\n\tassert.NoError(t, err)\n\tassert.NotNil(t, sk2)\n\tassert.NotNil(t, skProof2)\n\n\taccGroup1, memberDevice1, err := acc1.GetGroupForAccount()\n\tassert.NoError(t, err)\n\tassert.NotNil(t, accGroup1)\n\n\tmemberDevice2, err := acc2.GetOwnMemberDeviceForGroup(accGroup1)\n\tassert.NoError(t, err)\n\tassert.NotNil(t, memberDevice2)\n\n\tassert.True(t, memberDevice1.Member().Equals(memberDevice2.Member()))\n\tassert.False(t, memberDevice1.Device().Equals(memberDevice2.Device()))\n}\n\nfunc Test_ContactGroupPrivKey(t *testing.T) {\n\tacc1, err := secretstore.NewInMemSecretStore(nil)\n\tassert.NoError(t, err)\n\tassert.NotNil(t, acc1)\n\n\t_, acc1MemberDevice, err := acc1.GetGroupForAccount()\n\tassert.NoError(t, err)\n\tassert.NotNil(t, acc1MemberDevice)\n\n\tacc2, err := secretstore.NewInMemSecretStore(nil)\n\tassert.NoError(t, err)\n\tassert.NotNil(t, acc2)\n\n\t_, acc2MemberDevice, err := acc2.GetGroupForAccount()\n\tassert.NoError(t, err)\n\tassert.NotNil(t, acc2MemberDevice)\n\n\tgrp1, err := acc1.GetGroupForContact(acc2MemberDevice.Member())\n\tassert.NoError(t, err)\n\tassert.NotNil(t, grp1)\n\n\tgrp2, err := acc2.GetGroupForContact(acc1MemberDevice.Member())\n\tassert.NoError(t, err)\n\tassert.NotNil(t, grp2)\n\n\tassert.Equal(t, grp1.PublicKey, grp2.PublicKey)\n\tassert.Equal(t, grp1.Secret, grp2.Secret)\n\tassert.Equal(t, grp1.GroupType, grp2.GroupType)\n}\n\nfunc Test_MemberDeviceForGroup_multimember(t *testing.T) {\n\tacc1, err := secretstore.NewInMemSecretStore(nil)\n\tassert.NoError(t, err)\n\tassert.NotNil(t, acc1)\n\n\tsk1, skProof1, err := acc1.ExportAccountKeysForBackup()\n\tassert.NoError(t, err)\n\tassert.NotNil(t, sk1)\n\tassert.NotNil(t, skProof1)\n\n\tacc2, err := secretstore.NewInMemSecretStore(nil)\n\tassert.NoError(t, err)\n\tassert.NotNil(t, acc2)\n\n\terr = acc2.ImportAccountKeys(sk1, skProof1)\n\tassert.NoError(t, err)\n\n\tsk2, skProof2, err := acc2.ExportAccountKeysForBackup()\n\tassert.NoError(t, err)\n\tassert.NotNil(t, sk2)\n\tassert.NotNil(t, skProof2)\n\n\tg, _, err := protocoltypes.NewGroupMultiMember()\n\tassert.NoError(t, err)\n\n\tomd1, err := acc1.GetOwnMemberDeviceForGroup(g)\n\tassert.NoError(t, err)\n\n\tomd2, err := acc2.GetOwnMemberDeviceForGroup(g)\n\tassert.NoError(t, err)\n\n\tomd1M := omd1.Member()\n\tomd2M := omd2.Member()\n\tomd1D := omd1.Device()\n\tomd2D := omd2.Device()\n\n\tassert.True(t, omd1M.Equals(omd2M))\n\tassert.False(t, omd1D.Equals(omd2D))\n}\n"
  },
  {
    "path": "pkg/secretstore/doc.go",
    "content": "// Package secretstore contains function related to device, groups and messages keys.\npackage secretstore\n"
  },
  {
    "path": "pkg/secretstore/keys_utils.go",
    "content": "package secretstore\n\nimport (\n\t\"crypto/ed25519\"\n\t\"crypto/sha256\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/libp2p/go-libp2p/core/crypto\"\n\tcrypto_pb \"github.com/libp2p/go-libp2p/core/crypto/pb\"\n\t\"golang.org/x/crypto/hkdf\"\n\t\"golang.org/x/crypto/sha3\"\n\n\t\"berty.tech/weshnet/v2/pkg/cryptoutil\"\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n)\n\n// getEd25519PrivateKeyFromLibP2PFormattedBytes transforms an exported LibP2P\n// private key into a crypto.PrivKey instance, ensuring it is an ed25519 key\nfunc getEd25519PrivateKeyFromLibP2PFormattedBytes(rawKeyBytes []byte) (crypto.PrivKey, error) {\n\tif len(rawKeyBytes) == 0 {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"missing key data\"))\n\t}\n\n\tprivateKey, err := crypto.UnmarshalPrivateKey(rawKeyBytes)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(err)\n\t}\n\n\tif privateKey.Type() != crypto_pb.KeyType_Ed25519 {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"invalid key format\"))\n\t}\n\n\treturn privateKey, nil\n}\n\n// getKeysForGroupOfContact returns derived private keys for contact group\n// using a private key via two accounts account keys (via an ECDH).\nfunc getKeysForGroupOfContact(contactPairPrivateKey crypto.PrivKey) (crypto.PrivKey, crypto.PrivKey, error) {\n\t// Salt length must be equal to hash length (64 bytes for sha256)\n\thash := sha256.New\n\n\tcontactPairPrivateKeyBytes, err := contactPairPrivateKey.Raw()\n\tif err != nil {\n\t\treturn nil, nil, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\t// Generate Pseudo Random Key using contactPairPrivateKeyBytes as IKM and salt\n\tprk := hkdf.Extract(hash, contactPairPrivateKeyBytes, nil)\n\tif len(prk) == 0 {\n\t\treturn nil, nil, errcode.ErrCode_ErrInternal.Wrap(fmt.Errorf(\"unable to instantiate pseudo random key\"))\n\t}\n\n\t// Expand using extracted prk and groupID as info (kind of namespace)\n\tkdf := hkdf.Expand(hash, prk, nil)\n\n\t// Generate next KDF and message keys\n\tgroupSeed, err := io.ReadAll(io.LimitReader(kdf, 32))\n\tif err != nil {\n\t\treturn nil, nil, errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err)\n\t}\n\n\tgroupSecretSeed, err := io.ReadAll(io.LimitReader(kdf, 32))\n\tif err != nil {\n\t\treturn nil, nil, errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err)\n\t}\n\n\tstdGroupPrivateKey := ed25519.NewKeyFromSeed(groupSeed)\n\tgroupPrivateKey, _, err := crypto.KeyPairFromStdKey(&stdGroupPrivateKey)\n\tif err != nil {\n\t\treturn nil, nil, errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err)\n\t}\n\n\tstdGroupSecretPrivateKey := ed25519.NewKeyFromSeed(groupSecretSeed)\n\tgroupSecretPrivateKey, _, err := crypto.KeyPairFromStdKey(&stdGroupSecretPrivateKey)\n\tif err != nil {\n\t\treturn nil, nil, errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err)\n\t}\n\n\treturn groupPrivateKey, groupSecretPrivateKey, nil\n}\n\n// getGroupForContact returns a protocoltypes.Group instance for a contact,\n// using a private key via two accounts account keys (via an ECDH)\nfunc getGroupForContact(contactPairPrivateKey crypto.PrivKey) (*protocoltypes.Group, error) {\n\tgroupPrivateKey, groupSecretPrivateKey, err := getKeysForGroupOfContact(contactPairPrivateKey)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err)\n\t}\n\n\tpubBytes, err := groupPrivateKey.GetPublic().Raw()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\tsigningBytes, err := cryptoutil.SeedFromEd25519PrivateKey(groupSecretPrivateKey)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\treturn &protocoltypes.Group{\n\t\tPublicKey: pubBytes,\n\t\tSecret:    signingBytes,\n\t\tSecretSig: nil,\n\t\tGroupType: protocoltypes.GroupType_GroupTypeContact,\n\t}, nil\n}\n\n// getGroupOutOfStoreSecret retrieves the out of store group secret\nfunc getGroupOutOfStoreSecret(m *protocoltypes.Group) ([]byte, error) {\n\tif len(m.GetSecret()) == 0 {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"no secret known for group\"))\n\t}\n\n\tarr := [cryptoutil.KeySize]byte{}\n\n\tkdf := hkdf.New(sha3.New256, m.GetSecret(), nil, []byte(namespaceOutOfStoreSecret))\n\tif _, err := io.ReadFull(kdf, arr[:]); err != nil {\n\t\treturn nil, errcode.ErrCode_ErrStreamRead.Wrap(err)\n\t}\n\n\treturn arr[:], nil\n}\n\n// createOutOfStoreGroupReference creates a hash used to identify an out of\n// store (e.g. push notification) message origin\nfunc createOutOfStoreGroupReference(m *protocoltypes.Group, sender []byte, counter uint64) ([]byte, error) {\n\tsecret, err := getGroupOutOfStoreSecret(m)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(err)\n\t}\n\n\tarr := [cryptoutil.KeySize]byte{}\n\n\tbuf := make([]byte, 8)\n\tbinary.BigEndian.PutUint64(buf, counter)\n\n\tkdf := hkdf.New(sha3.New256, secret, nil, append(sender, buf...))\n\tif _, err := io.ReadFull(kdf, arr[:]); err != nil {\n\t\treturn nil, errcode.ErrCode_ErrStreamRead.Wrap(err)\n\t}\n\n\treturn arr[:], nil\n}\n"
  },
  {
    "path": "pkg/secretstore/member_device.go",
    "content": "package secretstore\n\nimport (\n\t\"github.com/libp2p/go-libp2p/core/crypto\"\n)\n\ntype ownMemberDevice struct {\n\tmember crypto.PrivKey\n\tdevice crypto.PrivKey\n\tpublic *memberDevice\n}\n\n// newOwnMemberDevice instantiate a new ownMemberDevice allowing signing\n// and encrypting data as both a device or a member part of a group.\n// It also contains the public counterpart of the member and device keys.\nfunc newOwnMemberDevice(member, device crypto.PrivKey) *ownMemberDevice {\n\treturn &ownMemberDevice{\n\t\tmember: member,\n\t\tdevice: device,\n\t\tpublic: newMemberDevice(member.GetPublic(), device.GetPublic()),\n\t}\n}\n\nfunc (d *ownMemberDevice) MemberSign(data []byte) ([]byte, error) {\n\treturn d.member.Sign(data)\n}\n\nfunc (d *ownMemberDevice) DeviceSign(data []byte) ([]byte, error) {\n\treturn d.device.Sign(data)\n}\n\nfunc (d *ownMemberDevice) Member() crypto.PubKey {\n\treturn d.public.member\n}\n\nfunc (d *ownMemberDevice) Device() crypto.PubKey {\n\treturn d.public.device\n}\n\ntype memberDevice struct {\n\tmember crypto.PubKey\n\tdevice crypto.PubKey\n}\n\nfunc NewMemberDevice(member, device crypto.PubKey) MemberDevice {\n\treturn newMemberDevice(member, device)\n}\n\nfunc newMemberDevice(member, device crypto.PubKey) *memberDevice {\n\treturn &memberDevice{\n\t\tmember: member,\n\t\tdevice: device,\n\t}\n}\n\nfunc (m *memberDevice) Member() crypto.PubKey {\n\treturn m.member\n}\n\nfunc (m *memberDevice) Device() crypto.PubKey {\n\treturn m.device\n}\n"
  },
  {
    "path": "pkg/secretstore/secret_store.go",
    "content": "package secretstore\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"sync\"\n\n\t\"github.com/ipfs/go-cid\"\n\t\"github.com/ipfs/go-datastore\"\n\tdssync \"github.com/ipfs/go-datastore/sync\"\n\t\"github.com/libp2p/go-libp2p/core/crypto\"\n\t\"go.uber.org/zap\"\n\t\"golang.org/x/crypto/nacl/secretbox\"\n\t\"google.golang.org/protobuf/proto\"\n\n\t\"berty.tech/weshnet/v2/internal/datastoreutil\"\n\t\"berty.tech/weshnet/v2/pkg/cryptoutil\"\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n\t\"berty.tech/weshnet/v2/pkg/ipfsutil\"\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n)\n\nconst (\n\tnamespaceDeviceKeystore   = \"device_keystore\"\n\tnamespaceOutOfStoreSecret = \"push_secret_ref\" // nolint:gosec\n)\n\ntype secretStore struct {\n\tlogger         *zap.Logger\n\tdatastore      datastore.Datastore\n\tdeviceKeystore *deviceKeystore\n\n\tmessageMutex sync.RWMutex\n\n\tpreComputedKeysCount               int\n\tprecomputeOutOfStoreGroupRefsCount uint64\n}\n\nfunc (o *NewSecretStoreOptions) applyDefaults(rootDatastore datastore.Datastore) {\n\tif o.Logger == nil {\n\t\to.Logger = zap.NewNop()\n\t}\n\n\tif o.Keystore == nil {\n\t\to.Keystore = ipfsutil.NewDatastoreKeystore(datastoreutil.NewNamespacedDatastore(rootDatastore, datastore.NewKey(namespaceDeviceKeystore)))\n\t}\n\n\tif o.PreComputedKeysCount <= 0 {\n\t\to.PreComputedKeysCount = PrecomputeMessageKeyCount\n\t}\n\n\tif o.PrecomputeOutOfStoreGroupRefsCount <= 0 {\n\t\to.PrecomputeOutOfStoreGroupRefsCount = PrecomputeOutOfStoreGroupRefsCount\n\t}\n}\n\n// NewSecretStore instantiates a new SecretStore\nfunc NewSecretStore(rootDatastore datastore.Datastore, opts *NewSecretStoreOptions) (SecretStore, error) {\n\treturn newSecretStore(rootDatastore, opts)\n}\n\n// newSecretStore instantiates a new secretStore\nfunc newSecretStore(rootDatastore datastore.Datastore, opts *NewSecretStoreOptions) (*secretStore, error) {\n\tif rootDatastore == nil {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"a datastore is required\"))\n\t}\n\n\tif opts == nil {\n\t\topts = &NewSecretStoreOptions{}\n\t}\n\n\topts.applyDefaults(rootDatastore)\n\n\tdevKeystore := newDeviceKeystore(opts.Keystore, opts.Logger)\n\n\tstore := &secretStore{\n\t\tlogger:         opts.Logger,\n\t\tdatastore:      rootDatastore,\n\t\tdeviceKeystore: devKeystore,\n\n\t\tpreComputedKeysCount:               opts.PreComputedKeysCount,\n\t\tprecomputeOutOfStoreGroupRefsCount: uint64(opts.PrecomputeOutOfStoreGroupRefsCount),\n\t}\n\n\treturn store, nil\n}\n\n// NewInMemSecretStore instantiates a SecretStore using a volatile backend.\nfunc NewInMemSecretStore(opts *NewSecretStoreOptions) (SecretStore, error) {\n\treturn newInMemSecretStore(opts)\n}\n\n// newInMemSecretStore instantiates a secretStore using a volatile backend.\nfunc newInMemSecretStore(opts *NewSecretStoreOptions) (*secretStore, error) {\n\treturn newSecretStore(dssync.MutexWrap(datastore.NewMapDatastore()), opts)\n}\n\nfunc (s *secretStore) Close() error {\n\treturn nil\n}\n\nfunc (s *secretStore) PutGroup(ctx context.Context, g *protocoltypes.Group) error {\n\tpk, err := g.GetPubKey()\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrInvalidInput.Wrap(err)\n\t}\n\n\t// TODO: check if partial group or full group and complete if necessary\n\tif ok, err := s.hasGroup(ctx, pk); err != nil {\n\t\treturn errcode.ErrCode_ErrInvalidInput.Wrap(err)\n\t} else if ok {\n\t\treturn nil\n\t}\n\n\tdata, err := proto.Marshal(g)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\tif err := s.datastore.Put(ctx, dsKeyForGroup(g.GetPublicKey()), data); err != nil {\n\t\treturn errcode.ErrCode_ErrKeystorePut.Wrap(err)\n\t}\n\n\tmemberDevice, err := s.GetOwnMemberDeviceForGroup(g)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\n\t// Force generation of chain key for own device\n\t_, err = s.GetShareableChainKey(ctx, g, memberDevice.Member())\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\n\treturn nil\n}\n\nfunc (s *secretStore) GetOwnMemberDeviceForGroup(g *protocoltypes.Group) (OwnMemberDevice, error) {\n\treturn s.deviceKeystore.memberDeviceForGroup(g)\n}\n\nfunc (s *secretStore) OpenOutOfStoreMessage(ctx context.Context, payload []byte) (*protocoltypes.OutOfStoreMessage, *protocoltypes.Group, []byte, bool, error) {\n\toosMessageEnv := &protocoltypes.OutOfStoreMessageEnvelope{}\n\tif err := proto.Unmarshal(payload, oosMessageEnv); err != nil {\n\t\treturn nil, nil, nil, false, errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\tgroupPublicKey, err := s.OutOfStoreGetGroupPublicKeyByGroupReference(ctx, oosMessageEnv.GroupReference)\n\tif err != nil {\n\t\treturn nil, nil, nil, false, errcode.ErrCode_ErrNotFound.Wrap(err)\n\t}\n\n\toosMessage, err := s.decryptOutOfStoreMessageEnv(ctx, oosMessageEnv, groupPublicKey)\n\tif err != nil {\n\t\treturn nil, nil, nil, false, errcode.ErrCode_ErrCryptoDecrypt.Wrap(err)\n\t}\n\n\tclear, newlyDecrypted, err := s.OutOfStoreMessageOpen(ctx, oosMessage, groupPublicKey)\n\tif err != nil {\n\t\treturn nil, nil, nil, false, errcode.ErrCode_ErrCryptoDecrypt.Wrap(err)\n\t}\n\n\tgroup, err := s.FetchGroupByPublicKey(ctx, groupPublicKey)\n\tif err == nil {\n\t\tif err := s.UpdateOutOfStoreGroupReferences(ctx, oosMessage.DevicePk, oosMessage.Counter, group); err != nil {\n\t\t\ts.logger.Error(\"unable to update push group references\", zap.Error(err))\n\t\t}\n\t}\n\n\treturn oosMessage, group, clear, !newlyDecrypted, nil\n}\n\nfunc (s *secretStore) decryptOutOfStoreMessageEnv(ctx context.Context, env *protocoltypes.OutOfStoreMessageEnvelope, groupPK crypto.PubKey) (*protocoltypes.OutOfStoreMessage, error) {\n\tnonce, err := cryptoutil.NonceSliceToArray(env.Nonce)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(err)\n\t}\n\n\tg, err := s.FetchGroupByPublicKey(ctx, groupPK)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"unable to find group, err: %w\", err))\n\t}\n\n\tsecret := g.GetSharedSecret()\n\n\tdata, ok := secretbox.Open(nil, env.Box, nonce, secret)\n\tif !ok {\n\t\treturn nil, errcode.ErrCode_ErrCryptoDecrypt.Wrap(fmt.Errorf(\"unable to decrypt message\"))\n\t}\n\n\toutOfStoreMessage := &protocoltypes.OutOfStoreMessage{}\n\tif err := proto.Unmarshal(data, outOfStoreMessage); err != nil {\n\t\treturn nil, errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\treturn outOfStoreMessage, nil\n}\n\nfunc (s *secretStore) FetchGroupByPublicKey(ctx context.Context, publicKey crypto.PubKey) (*protocoltypes.Group, error) {\n\tkeyBytes, err := publicKey.Raw()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\tdata, err := s.datastore.Get(ctx, dsKeyForGroup(keyBytes))\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrMissingMapKey.Wrap(err)\n\t}\n\n\tg := &protocoltypes.Group{}\n\tif err := proto.Unmarshal(data, g); err != nil {\n\t\treturn nil, errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\treturn g, nil\n}\n\nfunc (s *secretStore) GetAccountProofPublicKey() (crypto.PubKey, error) {\n\tprivateKey, err := s.deviceKeystore.getAccountPrivateKey()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\n\treturn privateKey.GetPublic(), nil\n}\n\nfunc (s *secretStore) ImportAccountKeys(accountPrivateKeyBytes []byte, accountProofPrivateKeyBytes []byte) error {\n\treturn s.deviceKeystore.restoreAccountKeys(accountPrivateKeyBytes, accountProofPrivateKeyBytes)\n}\n\nfunc (s *secretStore) ExportAccountKeysForBackup() (accountPrivateKeyBytes []byte, accountProofPrivateKeyBytes []byte, err error) {\n\taccountPrivateKey, err := s.deviceKeystore.getAccountPrivateKey()\n\tif err != nil {\n\t\treturn nil, nil, errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\n\taccountProofPrivateKey, err := s.deviceKeystore.getAccountProofPrivateKey()\n\tif err != nil {\n\t\treturn nil, nil, errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\n\taccountPrivateKeyBytes, err = crypto.MarshalPrivateKey(accountPrivateKey)\n\tif err != nil {\n\t\treturn nil, nil, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\taccountProofPrivateKeyBytes, err = crypto.MarshalPrivateKey(accountProofPrivateKey)\n\tif err != nil {\n\t\treturn nil, nil, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\treturn accountPrivateKeyBytes, accountProofPrivateKeyBytes, nil\n}\n\nfunc (s *secretStore) GetAccountPrivateKey() (crypto.PrivKey, error) {\n\taccountPrivateKey, err := s.deviceKeystore.getAccountPrivateKey()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\n\treturn accountPrivateKey, nil\n}\n\nfunc (s *secretStore) GetGroupForAccount() (*protocoltypes.Group, OwnMemberDevice, error) {\n\taccountPrivateKey, err := s.deviceKeystore.getAccountPrivateKey()\n\tif err != nil {\n\t\treturn nil, nil, errcode.ErrCode_ErrOrbitDBOpen.Wrap(err)\n\t}\n\n\taccountProofPrivateKey, err := s.deviceKeystore.getAccountProofPrivateKey()\n\tif err != nil {\n\t\treturn nil, nil, errcode.ErrCode_ErrOrbitDBOpen.Wrap(err)\n\t}\n\n\tdevicePrivateKey, err := s.deviceKeystore.devicePrivateKey()\n\tif err != nil {\n\t\treturn nil, nil, errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\n\tpubBytes, err := accountPrivateKey.GetPublic().Raw()\n\tif err != nil {\n\t\treturn nil, nil, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\tsigningBytes, err := cryptoutil.SeedFromEd25519PrivateKey(accountProofPrivateKey)\n\tif err != nil {\n\t\treturn nil, nil, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\treturn &protocoltypes.Group{\n\t\tPublicKey: pubBytes,\n\t\tSecret:    signingBytes,\n\t\tSecretSig: nil,\n\t\tGroupType: protocoltypes.GroupType_GroupTypeAccount,\n\t}, newOwnMemberDevice(accountPrivateKey, devicePrivateKey), nil\n}\n\nfunc (s *secretStore) GetGroupForContact(contactPublicKey crypto.PubKey) (*protocoltypes.Group, error) {\n\tcontactPairPrivateKey, err := s.deviceKeystore.contactGroupPrivateKey(contactPublicKey)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err)\n\t}\n\n\treturn getGroupForContact(contactPairPrivateKey)\n}\n\nfunc (s *secretStore) OpenEnvelopeHeaders(data []byte, g *protocoltypes.Group) (*protocoltypes.MessageEnvelope, *protocoltypes.MessageHeaders, error) {\n\tenv := &protocoltypes.MessageEnvelope{}\n\terr := proto.Unmarshal(data, env)\n\tif err != nil {\n\t\treturn nil, nil, errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\tnonce, err := cryptoutil.NonceSliceToArray(env.Nonce)\n\tif err != nil {\n\t\treturn nil, nil, errcode.ErrCode_ErrSerialization.Wrap(fmt.Errorf(\"unable to convert slice to array: %w\", err))\n\t}\n\n\theadersBytes, ok := secretbox.Open(nil, env.MessageHeaders, nonce, g.GetSharedSecret())\n\tif !ok {\n\t\treturn nil, nil, errcode.ErrCode_ErrCryptoDecrypt.Wrap(fmt.Errorf(\"secretbox failed to open headers\"))\n\t}\n\n\theaders := &protocoltypes.MessageHeaders{}\n\tif err := proto.Unmarshal(headersBytes, headers); err != nil {\n\t\treturn nil, nil, errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\treturn env, headers, nil\n}\n\nfunc (s *secretStore) SealOutOfStoreMessageEnvelope(id cid.Cid, env *protocoltypes.MessageEnvelope, headers *protocoltypes.MessageHeaders, group *protocoltypes.Group) (*protocoltypes.OutOfStoreMessageEnvelope, error) {\n\toosMessage := &protocoltypes.OutOfStoreMessage{\n\t\tCid:              id.Bytes(),\n\t\tDevicePk:         headers.DevicePk,\n\t\tCounter:          headers.Counter,\n\t\tSig:              headers.Sig,\n\t\tEncryptedPayload: env.Message,\n\t\tNonce:            env.Nonce,\n\t}\n\n\tdata, err := proto.Marshal(oosMessage)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\tnonce, err := cryptoutil.GenerateNonce()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrCryptoNonceGeneration.Wrap(err)\n\t}\n\n\tsecret, err := cryptoutil.KeySliceToArray(group.Secret)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrCryptoKeyConversion.Wrap(fmt.Errorf(\"unable to convert slice to array: %w\", err))\n\t}\n\n\tencryptedData := secretbox.Seal(nil, data, nonce, secret)\n\n\tpushGroupRef, err := createOutOfStoreGroupReference(group, headers.DevicePk, headers.Counter)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err)\n\t}\n\n\treturn &protocoltypes.OutOfStoreMessageEnvelope{\n\t\tNonce:          nonce[:],\n\t\tBox:            encryptedData,\n\t\tGroupReference: pushGroupRef,\n\t}, nil\n}\n\n// hasGroup checks whether a group is already known by the secretStore\nfunc (s *secretStore) hasGroup(ctx context.Context, key crypto.PubKey) (bool, error) {\n\tkeyBytes, err := key.Raw()\n\tif err != nil {\n\t\treturn false, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\treturn s.datastore.Has(ctx, dsKeyForGroup(keyBytes))\n}\n"
  },
  {
    "path": "pkg/secretstore/secret_store_interfaces.go",
    "content": "package secretstore\n\nimport (\n\t\"context\"\n\n\t\"github.com/ipfs/go-cid\"\n\tkeystore \"github.com/ipfs/go-ipfs-keystore\"\n\t\"github.com/libp2p/go-libp2p/core/crypto\"\n\t\"go.uber.org/zap\"\n\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n)\n\nconst (\n\tPrecomputeOutOfStoreGroupRefsCount = 100\n\tPrecomputeMessageKeyCount          = 100\n)\n\ntype messageKey [32]byte\n\ntype SecretStore interface {\n\t//\n\t// Account methods\n\t//\n\n\t// GetAccountProofPublicKey returns the user's account proof public key\n\tGetAccountProofPublicKey() (accountProofPublicKey crypto.PubKey, err error)\n\n\t// ImportAccountKeys restores backup of account keys into the SecretStore, it should fail if the store is already used by an account\n\tImportAccountKeys(accountPrivateKey []byte, accountProofPrivateKey []byte) error\n\n\t// ExportAccountKeysForBackup returns the account's private key and proof private key of the user for a backup\n\tExportAccountKeysForBackup() (accountPrivateKey []byte, accountProofPrivateKey []byte, err error)\n\n\t// GetAccountPrivateKey returns the account's private key, avoid using it, use GetGroupForAccount to get the account public key or sign data instead\n\tGetAccountPrivateKey() (accountPrivateKey crypto.PrivKey, err error)\n\n\t//\n\t// Groups methods\n\t//\n\n\t// GetGroupForAccount returns the Account's Group of the user\n\tGetGroupForAccount() (group *protocoltypes.Group, ownMemberDevice OwnMemberDevice, err error)\n\n\t// GetGroupForContact returns a contact group for communicating with the provided account\n\tGetGroupForContact(contactPublicKey crypto.PubKey) (group *protocoltypes.Group, err error)\n\n\t// PutGroup stores a group into the store\n\tPutGroup(ctx context.Context, group *protocoltypes.Group) error\n\n\t// FetchGroupByPublicKey gets an account from the store using the provided public key\n\tFetchGroupByPublicKey(ctx context.Context, publicKey crypto.PubKey) (group *protocoltypes.Group, err error)\n\n\t//\n\t// Envelopes methods\n\t//\n\n\t// OpenEnvelopeHeaders opens a message headers for a given group\n\tOpenEnvelopeHeaders(data []byte, group *protocoltypes.Group) (*protocoltypes.MessageEnvelope, *protocoltypes.MessageHeaders, error)\n\n\t// OpenEnvelopePayload opens a message payload with the given group headers\n\tOpenEnvelopePayload(ctx context.Context, msgEnvelope *protocoltypes.MessageEnvelope, msgHeaders *protocoltypes.MessageHeaders, groupPublicKey crypto.PubKey, ownPublicKey crypto.PubKey, msgCID cid.Cid) (*protocoltypes.EncryptedMessage, error)\n\n\t// SealEnvelope creates an encrypted payload to be sent to a group\n\tSealEnvelope(ctx context.Context, group *protocoltypes.Group, messagePayload []byte) (sealedEnvelope []byte, err error)\n\n\t//\n\t// Group member-device pairs methods\n\t//\n\n\t// GetOwnMemberDeviceForGroup gets a member and device key-pairs representing the current device in a given group\n\tGetOwnMemberDeviceForGroup(group *protocoltypes.Group) (OwnMemberDevice, error)\n\n\t//\n\t// Chain-keys methods\n\t//\n\n\t// RegisterChainKey records another device chain-key\n\tRegisterChainKey(ctx context.Context, group *protocoltypes.Group, senderDevicePublicKey crypto.PubKey, encryptedDeviceChainKey []byte) error\n\n\t// GetShareableChainKey returns a chain-key that can be decrypted by the provided member of a group\n\tGetShareableChainKey(ctx context.Context, group *protocoltypes.Group, targetMemberPublicKey crypto.PubKey) (encryptedDeviceChainKey []byte, err error)\n\n\t// IsChainKeyKnownForDevice checks whether a chain key of a device is already known\n\tIsChainKeyKnownForDevice(ctx context.Context, groupPublicKey crypto.PubKey, devicePublicKey crypto.PubKey) (isKnown bool)\n\n\t//\n\t// Out-of-store messages methods\n\t//\n\n\t// SealOutOfStoreMessageEnvelope encrypts a message to be sent outside a synchronized store\n\tSealOutOfStoreMessageEnvelope(id cid.Cid, env *protocoltypes.MessageEnvelope, headers *protocoltypes.MessageHeaders, group *protocoltypes.Group) (*protocoltypes.OutOfStoreMessageEnvelope, error)\n\n\t// OpenOutOfStoreMessage opens a message received outside a synchronized store\n\tOpenOutOfStoreMessage(ctx context.Context, payload []byte) (outOfStoreMessage *protocoltypes.OutOfStoreMessage, group *protocoltypes.Group, clearPayload []byte, alreadyDecrypted bool, err error)\n\n\t// UpdateOutOfStoreGroupReferences computes references of messages which might be received outside a synchronized store\n\tUpdateOutOfStoreGroupReferences(ctx context.Context, devicePublicKeyBytes []byte, first uint64, group *protocoltypes.Group) error\n\n\t// Close frees resources created by the secret store\n\tClose() error\n}\n\n// NewSecretStoreOptions contains the options that can be passed to NewSecretStore\ntype NewSecretStoreOptions struct {\n\t// PreComputedKeysCount specifies the number of keys to precompute,\n\t// defaults to PrecomputeMessageKeyCount\n\tPreComputedKeysCount int\n\n\t// PrecomputeOutOfStoreGroupRefsCount specifies the number of out of store references\n\t// to precompute, defaults to PrecomputeOutOfStoreGroupRefsCount\n\tPrecomputeOutOfStoreGroupRefsCount int\n\n\t// Keystore specifies an implementation of a keystore to be used, can be\n\t// helpful if you want to rely on a hardware based keystore instead of a\n\t// software one\n\tKeystore keystore.Keystore\n\n\t// Logger specifies which logger to use, logging is disabled by default\n\tLogger *zap.Logger\n\n\t// DisableOutOfStoreSupport explicitly disables support of out-of-store\n\t// payloads\n\tDisableOutOfStoreSupport bool\n}\n\n// MemberDevice is the public keys of a device and its member\ntype MemberDevice interface {\n\t// Member returns the member public key\n\tMember() crypto.PubKey\n\n\t// Device returns the device public key\n\tDevice() crypto.PubKey\n}\n\n// OwnMemberDevice is a MemberDevice for the current device, able to sign data\ntype OwnMemberDevice interface {\n\tMemberDevice\n\n\t// MemberSign signs the given data as a member of a group\n\tMemberSign(data []byte) ([]byte, error)\n\n\t// DeviceSign signs the given data as a device of a group\n\tDeviceSign(data []byte) ([]byte, error)\n}\n"
  },
  {
    "path": "pkg/secretstore/secret_store_messages.go",
    "content": "package secretstore\n\nimport (\n\t\"context\"\n\t\"crypto/sha256\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/ipfs/go-cid\"\n\t\"github.com/ipfs/go-datastore\"\n\t\"github.com/libp2p/go-libp2p/core/crypto\"\n\t\"go.uber.org/zap\"\n\t\"golang.org/x/crypto/hkdf\"\n\t\"golang.org/x/crypto/nacl/secretbox\"\n\t\"google.golang.org/protobuf/proto\"\n\n\t\"berty.tech/weshnet/v2/pkg/cryptoutil\"\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n\t\"berty.tech/weshnet/v2/pkg/logutil\"\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n)\n\n// decryptionContext contains context about a decrypted message, its CID, the\n// associated message key and whether it had been previously opened.\ntype decryptionContext struct {\n\tnewlyDecrypted bool\n\tmessageKey     *messageKey\n\tcid            cid.Cid\n}\n\n// computedMessageKey is a precomputed message key for a given counter used in the cache namespace.\ntype computedMessageKey struct {\n\tcounter    uint64\n\tmessageKey *messageKey\n}\n\n// getDeviceChainKeyForGroupAndDevice returns the device chain key for the given group and device.\nfunc (s *secretStore) getDeviceChainKeyForGroupAndDevice(ctx context.Context, groupPublicKey crypto.PubKey, devicePublicKey crypto.PubKey) (*protocoltypes.DeviceChainKey, error) {\n\tif s == nil {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"calling method of a non instantiated message keystore\"))\n\t}\n\n\tkey, err := dsKeyForCurrentChainKey(groupPublicKey, devicePublicKey)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\n\t// Not mutex here\n\tdsBytes, err := s.datastore.Get(ctx, key)\n\n\tif err == datastore.ErrNotFound {\n\t\treturn nil, errcode.ErrCode_ErrMissingInput.Wrap(err)\n\t} else if err != nil {\n\t\treturn nil, errcode.ErrCode_ErrMessageKeyPersistenceGet.Wrap(err)\n\t}\n\n\tds := &protocoltypes.DeviceChainKey{}\n\tif err := proto.Unmarshal(dsBytes, ds); err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(err)\n\t}\n\n\treturn ds, nil\n}\n\n// IsChainKeyKnownForDevice returns true if the device chain key is known for the given group and device.\nfunc (s *secretStore) IsChainKeyKnownForDevice(ctx context.Context, groupPublicKey crypto.PubKey, devicePublicKey crypto.PubKey) (has bool) {\n\tif s == nil {\n\t\treturn false\n\t}\n\n\tkey, err := dsKeyForCurrentChainKey(groupPublicKey, devicePublicKey)\n\tif err != nil {\n\t\treturn false\n\t}\n\n\ts.messageMutex.RLock()\n\tdefer s.messageMutex.RUnlock()\n\n\thas, _ = s.datastore.Has(ctx, key)\n\n\treturn\n}\n\n// delPrecomputedKey deletes the message key in the cache namespace for the given group, device and counter.\nfunc (s *secretStore) delPrecomputedKey(ctx context.Context, groupPublicKey crypto.PubKey, devicePublicKey crypto.PubKey, msgCounter uint64) error {\n\tif s == nil {\n\t\treturn errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"calling method of a non instantiated message keystore\"))\n\t}\n\n\tdevicePublicKeyRaw, err := devicePublicKey.Raw()\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\tgroupPublicKeyRaw, err := groupPublicKey.Raw()\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\tid := dsKeyForPrecomputedMessageKey(groupPublicKeyRaw, devicePublicKeyRaw, msgCounter)\n\n\terr = s.datastore.Delete(ctx, id)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrMessageKeyPersistencePut.Wrap(err)\n\t}\n\n\treturn nil\n}\n\n// postDecryptActions is called after a message has been decrypted.\n// It saves the message key from the cache namespace to find it quickly on subsequent read operations.\n// It derives the chain key in the cache namespace.\nfunc (s *secretStore) postDecryptActions(ctx context.Context, decryptionCtx *decryptionContext, groupPublicKey crypto.PubKey, ownPublicKey crypto.PubKey, msgHeaders *protocoltypes.MessageHeaders) error {\n\tif s == nil {\n\t\treturn errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"calling method of a non instantiated message keystore\"))\n\t}\n\n\t// Message was newly decrypted, we can save the message key and derive\n\t// future keys if necessary.\n\tif decryptionCtx == nil || !decryptionCtx.newlyDecrypted {\n\t\treturn nil\n\t}\n\n\tvar (\n\t\tdeviceChainKey *protocoltypes.DeviceChainKey\n\t\terr            error\n\t)\n\n\tdevicePublicKey, err := crypto.UnmarshalEd25519PublicKey(msgHeaders.DevicePk)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\tif err = s.putKeyForCID(ctx, decryptionCtx.cid, decryptionCtx.messageKey); err != nil {\n\t\treturn errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\n\tif err = s.delPrecomputedKey(ctx, groupPublicKey, devicePublicKey, msgHeaders.Counter); err != nil {\n\t\treturn errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\n\tif deviceChainKey, err = s.preComputeNextKey(ctx, groupPublicKey, devicePublicKey); err != nil {\n\t\treturn errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\n\t// If the message was not emitted by the current Device we might need\n\t// to update the current chain key\n\tif ownPublicKey == nil || !ownPublicKey.Equals(devicePublicKey) {\n\t\tif err = s.updateCurrentKey(ctx, groupPublicKey, devicePublicKey, deviceChainKey); err != nil {\n\t\t\treturn errcode.ErrCode_ErrInternal.Wrap(err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (s *secretStore) GetShareableChainKey(ctx context.Context, group *protocoltypes.Group, targetMemberPublicKey crypto.PubKey) ([]byte, error) {\n\tdeviceChainKey, err := s.getOwnDeviceChainKeyForGroup(ctx, group)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(err)\n\t}\n\n\tprivateMemberDevice, err := s.deviceKeystore.memberDeviceForGroup(group)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(err)\n\t}\n\n\tencryptedDeviceChainKey, err := encryptDeviceChainKey(privateMemberDevice.device, targetMemberPublicKey, deviceChainKey, group)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrCryptoEncrypt.Wrap(err)\n\t}\n\n\treturn encryptedDeviceChainKey, nil\n}\n\n// getOwnDeviceChainKeyForGroup returns the device chain key for the current\n// device on a given group.\n// If the chain key has not been created yet, it will be generated and\n// registered.\nfunc (s *secretStore) getOwnDeviceChainKeyForGroup(ctx context.Context, group *protocoltypes.Group) (*protocoltypes.DeviceChainKey, error) {\n\tif s == nil {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"calling method of a non instantiated message keystore\"))\n\t}\n\n\tif s.deviceKeystore == nil {\n\t\treturn nil, errcode.ErrCode_ErrCryptoSignature.Wrap(fmt.Errorf(\"message keystore is opened in read-only mode\"))\n\t}\n\n\tmd, err := s.deviceKeystore.memberDeviceForGroup(group)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\n\tgroupPublicKey, err := group.GetPubKey()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\ts.messageMutex.Lock()\n\tdefer s.messageMutex.Unlock()\n\n\tds, err := s.getDeviceChainKeyForGroupAndDevice(ctx, groupPublicKey, md.Device())\n\tif errcode.Is(err, errcode.ErrCode_ErrMissingInput) {\n\t\t// If secret does not exist, create it\n\t\tdeviceChainKey, err := newDeviceChainKey()\n\t\tif err != nil {\n\t\t\treturn nil, errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err)\n\t\t}\n\n\t\tif err = s.registerChainKey(ctx, group, md.Device(), deviceChainKey, true); err != nil {\n\t\t\treturn nil, errcode.ErrCode_ErrMessageKeyPersistencePut.Wrap(err)\n\t\t}\n\n\t\treturn deviceChainKey, nil\n\t}\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrMessageKeyPersistenceGet.Wrap(err)\n\t}\n\n\treturn ds, nil\n}\n\n// RegisterChainKey registers a chain key for the given group and device.\n// If the device chain key is not from the current device, the function will\n// precompute and store in the cache namespace the next message keys.\n// It is the exported version of registerChainKey.\nfunc (s *secretStore) RegisterChainKey(ctx context.Context, group *protocoltypes.Group, senderDevicePublicKey crypto.PubKey, encryptedDeviceChainKey []byte) error {\n\tif s == nil {\n\t\treturn errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"calling method of a non instantiated message keystore\"))\n\t}\n\n\tif s.deviceKeystore == nil {\n\t\treturn errcode.ErrCode_ErrCryptoSignature.Wrap(fmt.Errorf(\"message keystore is opened in read-only mode\"))\n\t}\n\n\tlocalMemberDevice, err := s.deviceKeystore.memberDeviceForGroup(group)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrGroupMemberUnknownGroupID.Wrap(err)\n\t}\n\n\tdeviceChainKey, err := decryptDeviceChainKey(encryptedDeviceChainKey, group, localMemberDevice.member, senderDevicePublicKey)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrCryptoDecrypt.Wrap(err)\n\t}\n\n\thasSecretBeenSentByCurrentDevice := localMemberDevice.Member().Equals(senderDevicePublicKey)\n\n\treturn s.registerChainKey(ctx, group, senderDevicePublicKey, deviceChainKey, hasSecretBeenSentByCurrentDevice)\n}\n\n// registerChainKey registers a chain key for the given group and device.\n// If the chain key is not from the current device, the function will\n// precompute and store in the cache namespace the next message keys.\nfunc (s *secretStore) registerChainKey(ctx context.Context, group *protocoltypes.Group, devicePublicKey crypto.PubKey, deviceChainKey *protocoltypes.DeviceChainKey, isCurrentDeviceChainKey bool) error {\n\tif s == nil {\n\t\treturn errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"calling method of a non instantiated message keystore\"))\n\t}\n\n\tgroupPublicKey, err := group.GetPubKey()\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\tif _, err := s.getDeviceChainKeyForGroupAndDevice(ctx, groupPublicKey, devicePublicKey); err == nil {\n\t\t// Device is already registered, ignore it\n\t\ts.logger.Debug(\"device already registered in group\",\n\t\t\tlogutil.PrivateBinary(\"devicePublicKey\", logutil.CryptoKeyToBytes(devicePublicKey)),\n\t\t\tlogutil.PrivateBinary(\"groupPublicKey\", logutil.CryptoKeyToBytes(groupPublicKey)),\n\t\t)\n\t\treturn nil\n\t}\n\n\ts.logger.Debug(\"registering chain key\",\n\t\tlogutil.PrivateBinary(\"devicePublicKey\", logutil.CryptoKeyToBytes(devicePublicKey)),\n\t\tlogutil.PrivateBinary(\"groupPublicKey\", logutil.CryptoKeyToBytes(groupPublicKey)),\n\t)\n\n\t// If own Device store key as is, no need to precompute future keys\n\tif isCurrentDeviceChainKey {\n\t\tif err := s.putDeviceChainKey(ctx, groupPublicKey, devicePublicKey, deviceChainKey); err != nil {\n\t\t\treturn errcode.ErrCode_ErrInternal.Wrap(err)\n\t\t}\n\n\t\treturn nil\n\t}\n\n\ts.messageMutex.Lock()\n\n\tif deviceChainKey, err = s.preComputeKeys(ctx, devicePublicKey, groupPublicKey, deviceChainKey); err != nil {\n\t\ts.messageMutex.Unlock()\n\t\treturn errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err)\n\t}\n\n\tif err := s.putDeviceChainKey(ctx, groupPublicKey, devicePublicKey, deviceChainKey); err != nil {\n\t\ts.messageMutex.Unlock()\n\t\treturn errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\n\ts.messageMutex.Unlock()\n\n\tdevicePublicKeyBytes, err := devicePublicKey.Raw()\n\tif err == nil {\n\t\tif err := s.UpdateOutOfStoreGroupReferences(ctx, devicePublicKeyBytes, deviceChainKey.Counter, group); err != nil {\n\t\t\ts.logger.Error(\"updating out of store group references failed\", zap.Error(err))\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// preComputeKeys precomputes the next m.preComputedKeysCount keys for the given device and group and put them in the cache namespace.\nfunc (s *secretStore) preComputeKeys(ctx context.Context, devicePublicKey crypto.PubKey, groupPublicKey crypto.PubKey, deviceChainKey *protocoltypes.DeviceChainKey) (*protocoltypes.DeviceChainKey, error) {\n\tif s == nil {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"calling method of a non instantiated message keystore\"))\n\t}\n\n\tchainKeyValue := deviceChainKey.ChainKey\n\tcounter := deviceChainKey.Counter\n\n\tgroupPublicKeyBytes, err := groupPublicKey.Raw()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\tknownDeviceChainKey, err := s.getDeviceChainKeyForGroupAndDevice(ctx, groupPublicKey, devicePublicKey)\n\tif err != nil && !errcode.Is(err, errcode.ErrCode_ErrMissingInput) {\n\t\treturn nil, errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\n\tvar preComputedKeys []computedMessageKey\n\tfor i := 0; i < s.getPrecomputedKeyExpectedCount(); i++ {\n\t\tcounter++\n\n\t\tknownMK, err := s.getPrecomputedMessageKey(ctx, groupPublicKey, devicePublicKey, counter)\n\t\tif err != nil && !errcode.Is(err, errcode.ErrCode_ErrMissingInput) {\n\t\t\treturn nil, errcode.ErrCode_ErrInternal.Wrap(err)\n\t\t}\n\n\t\tnewChainKeyValue, mk, err := deriveNextKeys(chainKeyValue, nil, groupPublicKeyBytes)\n\t\tif err != nil {\n\t\t\treturn nil, errcode.ErrCode_TODO.Wrap(err)\n\t\t}\n\n\t\tchainKeyValue = newChainKeyValue\n\n\t\tif knownMK != nil && knownDeviceChainKey != nil {\n\t\t\tif knownDeviceChainKey.Counter != counter-1 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\tpreComputedKeys = append(preComputedKeys, computedMessageKey{counter, &mk})\n\t}\n\n\terr = s.putPrecomputedKeys(ctx, groupPublicKey, devicePublicKey, preComputedKeys)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_TODO.Wrap(err)\n\t}\n\n\treturn &protocoltypes.DeviceChainKey{\n\t\tCounter:  counter,\n\t\tChainKey: chainKeyValue,\n\t}, nil\n}\n\n// getPrecomputedKeyExpectedCount returns the number of precomputed keys that\n// should be in the cache namespace of the keystore.\nfunc (s *secretStore) getPrecomputedKeyExpectedCount() int {\n\tif s == nil || s.preComputedKeysCount < 0 {\n\t\treturn 0\n\t}\n\n\treturn s.preComputedKeysCount\n}\n\n// preComputeNextKey precomputes the next key for the given group and device and adds it to the cache namespace.\nfunc (s *secretStore) preComputeNextKey(ctx context.Context, groupPublicKey crypto.PubKey, devicePublicKey crypto.PubKey) (*protocoltypes.DeviceChainKey, error) {\n\tif s == nil {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"calling method of a non instantiated message keystore\"))\n\t}\n\n\tif devicePublicKey == nil {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"devicePublicKey cannot be nil\"))\n\t}\n\n\tgroupPublicKeyBytes, err := groupPublicKey.Raw()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\tds, err := s.getDeviceChainKeyForGroupAndDevice(ctx, groupPublicKey, devicePublicKey)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\n\tnewCounter := ds.Counter + 1\n\n\t// TODO: Salt?\n\tnewCK, mk, err := deriveNextKeys(ds.ChainKey, nil, groupPublicKeyBytes)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_TODO.Wrap(err)\n\t}\n\n\terr = s.putPrecomputedKeys(ctx, groupPublicKey, devicePublicKey, []computedMessageKey{{newCounter, &mk}})\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_TODO.Wrap(err)\n\t}\n\n\treturn &protocoltypes.DeviceChainKey{\n\t\tCounter:  newCounter,\n\t\tChainKey: newCK,\n\t}, nil\n}\n\n// getPrecomputedMessageKey returns the precomputed message key put in the cache\n// namespace for the given group and device at the given counter.\nfunc (s *secretStore) getPrecomputedMessageKey(ctx context.Context, groupPublicKey crypto.PubKey, devicePublicKey crypto.PubKey, counter uint64) (*messageKey, error) {\n\tif s == nil {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"calling method of a non instantiated message keystore\"))\n\t}\n\n\tdeviceRaw, err := devicePublicKey.Raw()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\tgroupRaw, err := groupPublicKey.Raw()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\tid := dsKeyForPrecomputedMessageKey(groupRaw, deviceRaw, counter)\n\n\tkey, err := s.datastore.Get(ctx, id)\n\n\tif err == datastore.ErrNotFound {\n\t\treturn nil, errcode.ErrCode_ErrMissingInput.Wrap(fmt.Errorf(\"key for message does not exist in datastore\"))\n\t}\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrMessageKeyPersistenceGet.Wrap(err)\n\t}\n\n\tkeyArray, err := cryptoutil.KeySliceToArray(key)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\treturn (*messageKey)(keyArray), nil\n}\n\n// putPrecomputedKeys puts the given precomputed keys in the cache namespace.\n// It will try to use a batch if the store supports it.\nfunc (s *secretStore) putPrecomputedKeys(ctx context.Context, groupPublicKey crypto.PubKey, devicePublicKey crypto.PubKey, preComputedMessageKeys []computedMessageKey) error {\n\tif s == nil {\n\t\treturn errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"calling method of a non instantiated message keystore\"))\n\t}\n\n\ts.logger.Debug(\"putting precomputed keys\", zap.Int(\"count\", len(preComputedMessageKeys)))\n\n\tif len(preComputedMessageKeys) == 0 {\n\t\treturn nil\n\t}\n\n\tdeviceRaw, err := devicePublicKey.Raw()\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\tgroupRaw, err := groupPublicKey.Raw()\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\tif batchedDatastore, ok := s.datastore.(datastore.BatchingFeature); ok {\n\t\tbatch, err := batchedDatastore.Batch(ctx)\n\t\tif err == datastore.ErrBatchUnsupported {\n\t\t\treturn s.putPrecomputedKeysNonBatched(ctx, groupRaw, deviceRaw, preComputedMessageKeys)\n\t\t}\n\n\t\treturn s.putPrecomputedKeysBatched(ctx, batch, groupRaw, deviceRaw, preComputedMessageKeys)\n\t}\n\n\treturn s.putPrecomputedKeysNonBatched(ctx, groupRaw, deviceRaw, preComputedMessageKeys)\n}\n\nfunc (s *secretStore) putPrecomputedKeysBatched(ctx context.Context, batch datastore.Batch, groupRaw []byte, deviceRaw []byte, preComputedMessageKeys []computedMessageKey) error {\n\tfor _, preComputedKey := range preComputedMessageKeys {\n\t\tid := dsKeyForPrecomputedMessageKey(groupRaw, deviceRaw, preComputedKey.counter)\n\n\t\tif err := batch.Put(ctx, id, preComputedKey.messageKey[:]); err != nil {\n\t\t\treturn errcode.ErrCode_ErrMessageKeyPersistencePut.Wrap(err)\n\t\t}\n\t}\n\n\tif err := batch.Commit(ctx); err != nil {\n\t\treturn errcode.ErrCode_ErrMessageKeyPersistencePut.Wrap(err)\n\t}\n\n\treturn nil\n}\n\nfunc (s *secretStore) putPrecomputedKeysNonBatched(ctx context.Context, groupRaw []byte, deviceRaw []byte, preComputedMessageKeys []computedMessageKey) error {\n\tfor _, preComputedKey := range preComputedMessageKeys {\n\t\tid := dsKeyForPrecomputedMessageKey(groupRaw, deviceRaw, preComputedKey.counter)\n\n\t\tif err := s.datastore.Put(ctx, id, preComputedKey.messageKey[:]); err != nil {\n\t\t\treturn errcode.ErrCode_ErrMessageKeyPersistencePut.Wrap(err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// putKeyForCID puts the given message key in the datastore for a specified CID.\nfunc (s *secretStore) putKeyForCID(ctx context.Context, messageCID cid.Cid, messageKey *messageKey) error {\n\tif s == nil {\n\t\treturn errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"calling method of a non instantiated message keystore\"))\n\t}\n\n\tif !messageCID.Defined() {\n\t\treturn nil\n\t}\n\n\terr := s.datastore.Put(ctx, dsKeyForMessageKeyByCID(messageCID), messageKey[:])\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrMessageKeyPersistencePut.Wrap(err)\n\t}\n\n\treturn nil\n}\n\n// OpenEnvelopePayload opens the payload of a message envelope and returns the\n// decrypted message in its EncryptedMessage form.\n// It also performs post decryption actions such as updating message key cache.\nfunc (s *secretStore) OpenEnvelopePayload(ctx context.Context, msgEnvelope *protocoltypes.MessageEnvelope, msgHeaders *protocoltypes.MessageHeaders, groupPublicKey crypto.PubKey, ownDevicePublicKey crypto.PubKey, msgCID cid.Cid) (*protocoltypes.EncryptedMessage, error) {\n\ts.messageMutex.Lock()\n\tdefer s.messageMutex.Unlock()\n\n\tmsgBytes, decryptionCtx, err := s.openPayload(ctx, msgCID, groupPublicKey, msgEnvelope.Message, msgHeaders)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrCryptoDecryptPayload.Wrap(err)\n\t}\n\n\tif err := s.postDecryptActions(ctx, decryptionCtx, groupPublicKey, ownDevicePublicKey, msgHeaders); err != nil {\n\t\treturn nil, errcode.ErrCode_TODO.Wrap(err)\n\t}\n\n\tvar msg protocoltypes.EncryptedMessage\n\terr = proto.Unmarshal(msgBytes, &msg)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\treturn &msg, nil\n}\n\n// openPayload opens the payload of a message envelope and returns the\n// decrypted message.\n// It retrieves the message key from the keystore or the cache to decrypt\n// the message.\nfunc (s *secretStore) openPayload(ctx context.Context, msgCID cid.Cid, groupPublicKey crypto.PubKey, payload []byte, msgHeaders *protocoltypes.MessageHeaders) ([]byte, *decryptionContext, error) {\n\tif s == nil {\n\t\treturn nil, nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"calling method of a non instantiated message keystore\"))\n\t}\n\n\tvar (\n\t\terr           error\n\t\tdecryptionCtx = &decryptionContext{\n\t\t\tcid:            msgCID,\n\t\t\tnewlyDecrypted: true,\n\t\t}\n\t\tpublicKey crypto.PubKey\n\t)\n\n\tif decryptionCtx.messageKey, err = s.getKeyForCID(ctx, msgCID); err == nil {\n\t\tdecryptionCtx.newlyDecrypted = false\n\t} else {\n\t\tpublicKey, err = crypto.UnmarshalEd25519PublicKey(msgHeaders.DevicePk)\n\t\tif err != nil {\n\t\t\treturn nil, nil, errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t\t}\n\n\t\tdecryptionCtx.messageKey, err = s.getPrecomputedMessageKey(ctx, groupPublicKey, publicKey, msgHeaders.Counter)\n\t\tif err != nil {\n\t\t\treturn nil, nil, errcode.ErrCode_ErrCryptoDecrypt.Wrap(err)\n\t\t}\n\t}\n\n\treturn s.openPayloadWithMessageKey(decryptionCtx, publicKey, payload, msgHeaders)\n}\n\n// openPayloadWithMessageKey opens the payload of a message envelope with the\n// given key and returns the decrypted message with the decryptionContext\n// struct.\nfunc (s *secretStore) openPayloadWithMessageKey(decryptionCtx *decryptionContext, devicePublicKey crypto.PubKey, payload []byte, headers *protocoltypes.MessageHeaders) ([]byte, *decryptionContext, error) {\n\tmsg, ok := secretbox.Open(nil, payload, uint64AsNonce(headers.Counter), (*[32]byte)(decryptionCtx.messageKey))\n\tif !ok {\n\t\treturn nil, nil, errcode.ErrCode_ErrCryptoDecrypt.Wrap(fmt.Errorf(\"secret box failed to open message payload\"))\n\t}\n\n\tif decryptionCtx.newlyDecrypted {\n\t\tif ok, err := devicePublicKey.Verify(msg, headers.Sig); !ok {\n\t\t\treturn nil, nil, errcode.ErrCode_ErrCryptoSignatureVerification.Wrap(fmt.Errorf(\"unable to verify message signature\"))\n\t\t} else if err != nil {\n\t\t\treturn nil, nil, errcode.ErrCode_ErrCryptoSignatureVerification.Wrap(err)\n\t\t}\n\t}\n\n\t// Message was newly decrypted, we can save the message key and derive\n\t// future keys if necessary.\n\treturn msg, decryptionCtx, nil\n}\n\n// getKeyForCID retrieves the message key for the given message CID.\nfunc (s *secretStore) getKeyForCID(ctx context.Context, msgCID cid.Cid) (*messageKey, error) {\n\tif s == nil {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"calling method of a non instantiated message keystore\"))\n\t}\n\n\tif !msgCID.Defined() {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"undefined message CID\"))\n\t}\n\n\tmsgKey, err := s.datastore.Get(ctx, dsKeyForMessageKeyByCID(msgCID))\n\n\tif err == datastore.ErrNotFound {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(err)\n\t}\n\n\tmsgKeyArray, err := cryptoutil.KeySliceToArray(msgKey)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\treturn (*messageKey)(msgKeyArray), nil\n}\n\n// putDeviceChainKey stores the chain key for the given group and device.\nfunc (s *secretStore) putDeviceChainKey(ctx context.Context, groupPublicKey crypto.PubKey, devicePublicKey crypto.PubKey, deviceChainKey *protocoltypes.DeviceChainKey) error {\n\tif s == nil {\n\t\treturn errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"calling method of a non instantiated message keystore\"))\n\t}\n\n\tdeviceChainKeyBytes, err := proto.Marshal(deviceChainKey)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\tdatastoreKey, err := dsKeyForCurrentChainKey(groupPublicKey, devicePublicKey)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\terr = s.datastore.Put(ctx, datastoreKey, deviceChainKeyBytes)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrMessageKeyPersistencePut.Wrap(err)\n\t}\n\n\treturn nil\n}\n\n// SealEnvelope encrypts the given payload and returns it as an envelope to be\n// published on the group's store.\n// It retrieves the device's chain key from the keystore to encrypt the payload\n// using symmetric encryption. The payload is signed using the device's long\n// term private key for the target group. It also updates the chain key and\n// stores the next message key in the cache.\nfunc (s *secretStore) SealEnvelope(ctx context.Context, group *protocoltypes.Group, messagePayload []byte) ([]byte, error) {\n\tif s == nil {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"calling method of a non instantiated message keystore\"))\n\t}\n\n\tif s.deviceKeystore == nil {\n\t\treturn nil, errcode.ErrCode_ErrCryptoSignature.Wrap(fmt.Errorf(\"message keystore is opened in read-only mode\"))\n\t}\n\n\tif group == nil {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"group cannot be nil\"))\n\t}\n\n\tlocalMemberDevice, err := s.deviceKeystore.memberDeviceForGroup(group)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrGroupMemberUnknownGroupID.Wrap(err)\n\t}\n\n\tgroupPublicKey, err := group.GetPubKey()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\ts.messageMutex.Lock()\n\tdefer s.messageMutex.Unlock()\n\n\tdeviceChainKey, err := s.getDeviceChainKeyForGroupAndDevice(ctx, groupPublicKey, localMemberDevice.Device())\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInternal.Wrap(fmt.Errorf(\"unable to get device chainkey: %w\", err))\n\t}\n\n\tenv, err := sealEnvelope(messagePayload, deviceChainKey, localMemberDevice.device, group)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrCryptoEncrypt.Wrap(fmt.Errorf(\"unable to seal envelope: %w\", err))\n\t}\n\n\tif err := s.deriveDeviceChainKey(ctx, group, localMemberDevice.Device()); err != nil {\n\t\treturn nil, errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err)\n\t}\n\n\treturn env, nil\n}\n\n// deriveDeviceChainKey derives the chain key value from the current one.\n// It also updates the device chain key in the keystore.\nfunc (s *secretStore) deriveDeviceChainKey(ctx context.Context, group *protocoltypes.Group, devicePublicKey crypto.PubKey) error {\n\tif s == nil {\n\t\treturn errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"calling method of a non instantiated message keystore\"))\n\t}\n\n\tif devicePublicKey == nil {\n\t\treturn errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"device public key cannot be nil\"))\n\t}\n\n\tgroupPublicKey, err := group.GetPubKey()\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\tdeviceChainKey, err := s.preComputeNextKey(ctx, groupPublicKey, devicePublicKey)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err)\n\t}\n\n\tif err = s.updateCurrentKey(ctx, groupPublicKey, devicePublicKey, deviceChainKey); err != nil {\n\t\treturn errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err)\n\t}\n\n\treturn nil\n}\n\n// updateCurrentKey updates the current device chain key in the keystore if the\n// given device secret has a higher counter.\nfunc (s *secretStore) updateCurrentKey(ctx context.Context, groupPublicKey crypto.PubKey, devicePublicKey crypto.PubKey, deviceChainKey *protocoltypes.DeviceChainKey) error {\n\tif s == nil {\n\t\treturn errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"calling method of a non instantiated message keystore\"))\n\t}\n\n\tcurrentDeviceChainKey, err := s.getDeviceChainKeyForGroupAndDevice(ctx, groupPublicKey, devicePublicKey)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\n\t// FIXME: counter is set randomly and can overflow to 0\n\tif deviceChainKey.Counter < currentDeviceChainKey.Counter {\n\t\treturn nil\n\t}\n\n\tif err = s.putDeviceChainKey(ctx, groupPublicKey, devicePublicKey, deviceChainKey); err != nil {\n\t\treturn errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\n\treturn nil\n}\n\n// OutOfStoreMessageOpen opens the given OutOfStoreMessage and returns the\n// decrypted payload.\n// The signature is verified against the given groupPublicKey.\n// It derives the next message key and stores it in the cache, but it doesn't\n// update the device's chain key.\nfunc (s *secretStore) OutOfStoreMessageOpen(ctx context.Context, envelope *protocoltypes.OutOfStoreMessage, groupPublicKey crypto.PubKey) ([]byte, bool, error) {\n\tif s == nil {\n\t\treturn nil, false, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"calling method of a non instantiated message keystore\"))\n\t}\n\n\tif envelope == nil {\n\t\treturn nil, false, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"envelope cannot be nil\"))\n\t}\n\n\tif groupPublicKey == nil {\n\t\treturn nil, false, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"group public key cannot be nil\"))\n\t}\n\n\tdevicePublicKey, err := crypto.UnmarshalEd25519PublicKey(envelope.DevicePk)\n\tif err != nil {\n\t\treturn nil, false, errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\tc := cid.Undef\n\tif len(envelope.Cid) > 0 {\n\t\t_, c, err = cid.CidFromBytes(envelope.Cid)\n\t\tif err != nil {\n\t\t\treturn nil, false, errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t\t}\n\t}\n\n\ts.messageMutex.Lock()\n\tdefer s.messageMutex.Unlock()\n\n\tdecryptionCtx := &decryptionContext{newlyDecrypted: true}\n\tif decryptionCtx.messageKey, err = s.getKeyForCID(ctx, c); err == nil {\n\t\tdecryptionCtx.newlyDecrypted = false\n\t} else {\n\t\tdecryptionCtx.messageKey, err = s.getPrecomputedMessageKey(ctx, groupPublicKey, devicePublicKey, envelope.Counter)\n\t\tif err != nil {\n\t\t\treturn nil, false, errcode.ErrCode_ErrCryptoDecrypt.Wrap(err)\n\t\t}\n\t}\n\n\tclear, decryptionCtx, err := s.openPayloadWithMessageKey(decryptionCtx, devicePublicKey, envelope.EncryptedPayload, &protocoltypes.MessageHeaders{\n\t\tCounter:  envelope.Counter,\n\t\tDevicePk: envelope.DevicePk,\n\t\tSig:      envelope.Sig,\n\t})\n\tif err != nil {\n\t\treturn nil, false, errcode.ErrCode_ErrCryptoDecrypt.Wrap(err)\n\t}\n\n\tif ok, err := devicePublicKey.Verify(clear, envelope.Sig); !ok {\n\t\treturn nil, false, errcode.ErrCode_ErrCryptoSignatureVerification.Wrap(fmt.Errorf(\"unable to verify message signature\"))\n\t} else if err != nil {\n\t\treturn nil, false, errcode.ErrCode_ErrCryptoSignatureVerification.Wrap(err)\n\t}\n\n\tif _, err = s.preComputeNextKey(ctx, groupPublicKey, devicePublicKey); err != nil {\n\t\treturn nil, false, errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\n\treturn clear, decryptionCtx.newlyDecrypted, nil\n}\n\n// OutOfStoreGetGroupPublicKeyByGroupReference returns the group public key\n// associated with the given out of store group reference (e.g. push\n// notification payload).\nfunc (s *secretStore) OutOfStoreGetGroupPublicKeyByGroupReference(ctx context.Context, ref []byte) (crypto.PubKey, error) {\n\ts.messageMutex.RLock()\n\tpk, err := s.datastore.Get(ctx, dsKeyForOutOfStoreMessageGroupHint(ref))\n\ts.messageMutex.RUnlock()\n\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrNotFound.Wrap(err)\n\t}\n\n\tgroupPublicKey, err := crypto.UnmarshalEd25519PublicKey(pk)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\treturn groupPublicKey, nil\n}\n\n// UpdateOutOfStoreGroupReferences updates the out of store (e.g. push\n// notification payload) group references for the given devicePublicKey and\n// groupPublicKey in  the keystore. It creates the references for the\n// given range [first + precomputeOutOfStoreGroupRefsCount] and\n// [first - precomputeOutOfStoreGroupRefsCount] and deletes out of range\n// references.\nfunc (s *secretStore) UpdateOutOfStoreGroupReferences(ctx context.Context, devicePublicKey []byte, first uint64, group *protocoltypes.Group) error {\n\ts.messageMutex.Lock()\n\tdefer s.messageMutex.Unlock()\n\n\trefsExisting := []uint64(nil)\n\trefsToCreate := []uint64(nil)\n\n\tcurrentFirst, currentLast, err := s.firstLastCachedGroupRefsForMember(ctx, devicePublicKey, group)\n\tif err == nil {\n\t\tfor i := currentFirst; i != currentLast; i++ {\n\t\t\trefsExisting = append(refsExisting, i)\n\t\t}\n\t}\n\n\t// keep previous refs\n\tlast := first + s.precomputeOutOfStoreGroupRefsCount\n\tfirst -= s.precomputeOutOfStoreGroupRefsCount\n\tfor i := first; i != last; i++ {\n\t\tfound := false\n\n\t\t// Ignore refs that should be kept\n\t\tfor j := 0; j < len(refsExisting); j++ {\n\t\t\tif refsExisting[j] == i {\n\t\t\t\trefsExisting[j] = refsExisting[len(refsExisting)-1]\n\t\t\t\trefsExisting = refsExisting[:len(refsExisting)-1]\n\t\t\t\tfound = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tif !found {\n\t\t\trefsToCreate = append(refsToCreate, i)\n\t\t}\n\t}\n\n\t// Remove useless old refs\n\tfor i := 0; i < len(refsExisting); i++ {\n\t\tref, err := createOutOfStoreGroupReference(group, devicePublicKey, refsExisting[i])\n\t\tif err != nil {\n\t\t\ts.logger.Error(\"creating existing out of store group reference failed\", logutil.PrivateBinary(\"ref\", ref), zap.Error(err))\n\t\t\tcontinue\n\t\t}\n\n\t\tif err := s.datastore.Delete(ctx, dsKeyForOutOfStoreMessageGroupHint(ref)); err != nil {\n\t\t\ts.logger.Error(\"deleting existing out of store group reference failed\", logutil.PrivateBinary(\"ref\", ref), zap.Error(err))\n\t\t\tcontinue\n\t\t}\n\t}\n\n\t// Add new refs\n\tfor i := 0; i < len(refsToCreate); i++ {\n\t\tref, err := createOutOfStoreGroupReference(group, devicePublicKey, refsToCreate[i])\n\t\tif err != nil {\n\t\t\ts.logger.Error(\"creating new out of store group reference failed\", logutil.PrivateBinary(\"ref\", ref), zap.Error(err))\n\t\t\tcontinue\n\t\t}\n\n\t\tif err := s.datastore.Put(ctx, dsKeyForOutOfStoreMessageGroupHint(ref), group.GetPublicKey()); err != nil {\n\t\t\ts.logger.Error(\"putting new out of store group reference failed\", logutil.PrivateBinary(\"ref\", ref), zap.Error(err))\n\t\t\tcontinue\n\t\t}\n\t}\n\n\t// Update first/last\n\tif err := s.putFirstLastCachedGroupRefsForMember(ctx, first, last, devicePublicKey, group); err != nil {\n\t\ts.logger.Error(\"putting first/last out of store group reference failed\", zap.Error(err))\n\t}\n\n\treturn nil\n}\n\n// firstLastCachedGroupRefsForMember returns the first and last cached group\n// references counter for the given devicePublicKey and group.\nfunc (s *secretStore) firstLastCachedGroupRefsForMember(ctx context.Context, devicePublicKeyBytes []byte, group *protocoltypes.Group) (uint64, uint64, error) {\n\tkey := dsKeyForOutOfStoreFirstLastCounters(group.GetPublicKey(), devicePublicKeyBytes)\n\n\t// No mutex here\n\tbytes, err := s.datastore.Get(ctx, key)\n\tif err != nil {\n\t\treturn 0, 0, errcode.ErrCode_ErrDBRead.Wrap(err)\n\t}\n\n\tret := protocoltypes.FirstLastCounters{}\n\tif err := proto.Unmarshal(bytes, &ret); err != nil {\n\t\treturn 0, 0, errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\treturn ret.First, ret.Last, nil\n}\n\n// putFirstLastCachedGroupRefsForMember puts the first and last cached group\n// references counter for the given devicePK and groupPK.\nfunc (s *secretStore) putFirstLastCachedGroupRefsForMember(ctx context.Context, first uint64, last uint64, devicePublicKey []byte, group *protocoltypes.Group) error {\n\tkey := dsKeyForOutOfStoreFirstLastCounters(group.GetPublicKey(), devicePublicKey)\n\n\tfistLast := protocoltypes.FirstLastCounters{\n\t\tFirst: first,\n\t\tLast:  last,\n\t}\n\tbytes, err := proto.Marshal(&fistLast)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\t// Not mutex here\n\treturn s.datastore.Put(ctx, key, bytes)\n}\n\nfunc sealPayload(payload []byte, ds *protocoltypes.DeviceChainKey, devicePrivateKey crypto.PrivKey, g *protocoltypes.Group) ([]byte, []byte, error) {\n\tvar (\n\t\tmsgKey [32]byte\n\t\terr    error\n\t)\n\n\tsig, err := devicePrivateKey.Sign(payload)\n\tif err != nil {\n\t\treturn nil, nil, errcode.ErrCode_ErrCryptoSignature.Wrap(err)\n\t}\n\n\tif _, msgKey, err = deriveNextKeys(ds.ChainKey, nil, g.GetPublicKey()); err != nil {\n\t\treturn nil, nil, errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err)\n\t}\n\n\treturn secretbox.Seal(nil, payload, uint64AsNonce(ds.Counter+1), &msgKey), sig, nil\n}\n\nfunc sealEnvelope(messagePayload []byte, deviceChainKey *protocoltypes.DeviceChainKey, devicePrivateKey crypto.PrivKey, g *protocoltypes.Group) ([]byte, error) {\n\tencryptedPayload, sig, err := sealPayload(messagePayload, deviceChainKey, devicePrivateKey, g)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrCryptoEncrypt.Wrap(err)\n\t}\n\n\tdevicePublicKeyRaw, err := devicePrivateKey.GetPublic().Raw()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\th := &protocoltypes.MessageHeaders{\n\t\tCounter:  deviceChainKey.Counter + 1,\n\t\tDevicePk: devicePublicKeyRaw,\n\t\tSig:      sig,\n\t}\n\n\theaders, err := proto.Marshal(h)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\tnonce, err := cryptoutil.GenerateNonce()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrCryptoNonceGeneration.Wrap(err)\n\t}\n\n\tencryptedHeaders := secretbox.Seal(nil, headers, nonce, g.GetSharedSecret())\n\n\tenv, err := proto.Marshal(&protocoltypes.MessageEnvelope{\n\t\tMessageHeaders: encryptedHeaders,\n\t\tMessage:        encryptedPayload,\n\t\tNonce:          nonce[:],\n\t})\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\treturn env, nil\n}\n\n// nolint:unparam\nfunc deriveNextKeys(chainKeyValue []byte, salt []byte, groupID []byte) ([]byte, messageKey, error) {\n\tvar (\n\t\tnextMsg [32]byte\n\t\terr     error\n\t)\n\n\t// Salt length must be equal to hash length (64 bytes for sha256)\n\thash := sha256.New\n\n\t// Generate Pseudo Random Key using chainKeyValue as IKM and salt\n\tprk := hkdf.Extract(hash, chainKeyValue, salt)\n\tif len(prk) == 0 {\n\t\treturn nil, nextMsg, errcode.ErrCode_ErrInternal.Wrap(fmt.Errorf(\"unable to instantiate pseudo random key\"))\n\t}\n\n\t// Expand using extracted prk and groupID as info (kind of namespace)\n\tkdf := hkdf.Expand(hash, prk, groupID)\n\n\t// Generate next KDF and message keys\n\tnextCK, err := io.ReadAll(io.LimitReader(kdf, 32))\n\tif err != nil {\n\t\treturn nil, nextMsg, errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err)\n\t}\n\n\tnextMsgSlice, err := io.ReadAll(io.LimitReader(kdf, 32))\n\tif err != nil {\n\t\treturn nil, nextMsg, errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err)\n\t}\n\n\tcopy(nextMsg[:], nextMsgSlice)\n\n\treturn nextCK, nextMsg, nil\n}\n\nfunc uint64AsNonce(val uint64) *[24]byte {\n\tvar nonce [24]byte\n\n\tbinary.BigEndian.PutUint64(nonce[:], val)\n\n\treturn &nonce\n}\n"
  },
  {
    "path": "pkg/secretstore/secret_store_messages_test.go",
    "content": "package secretstore_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"path\"\n\t\"testing\"\n\t\"time\"\n\n\tcid \"github.com/ipfs/go-cid\"\n\t\"github.com/libp2p/go-libp2p/core/crypto\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"google.golang.org/protobuf/proto\"\n\n\t\"berty.tech/weshnet/v2\"\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n\t\"berty.tech/weshnet/v2/pkg/secretstore\"\n\t\"berty.tech/weshnet/v2/pkg/testutil\"\n)\n\nfunc addDummyMemberInMetadataStore(ctx context.Context, t testing.TB, ms *weshnet.MetadataStore, g *protocoltypes.Group, memberPK crypto.PubKey, join bool) crypto.PubKey {\n\tt.Helper()\n\n\tsecretStore, err := secretstore.NewInMemSecretStore(nil)\n\tassert.NoError(t, err)\n\tassert.NotNil(t, secretStore)\n\n\tmd, err := secretStore.GetOwnMemberDeviceForGroup(g)\n\tassert.NoError(t, err)\n\n\tif join {\n\t\t_, err = weshnet.MetadataStoreAddDeviceToGroup(ctx, ms, g, md)\n\t\tassert.NoError(t, err)\n\t}\n\n\tdeviceChainKeyToSend, err := secretStore.GetShareableChainKey(ctx, g, memberPK)\n\tassert.NoError(t, err)\n\n\t_, err = weshnet.MetadataStoreSendSecret(ctx, ms, g, md, memberPK, deviceChainKeyToSend)\n\tassert.NoError(t, err)\n\n\treturn md.Device()\n}\n\nfunc Test_EncryptMessageEnvelope(t *testing.T) {\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\tg, _, err := weshnet.NewGroupMultiMember()\n\tassert.NoError(t, err)\n\n\tsecretStore1, err := secretstore.NewInMemSecretStore(nil)\n\tassert.NoError(t, err)\n\tassert.NotNil(t, secretStore1)\n\n\tt.Cleanup(func() {\n\t\t_ = secretStore1.Close()\n\t})\n\n\tomd1, err := secretStore1.GetOwnMemberDeviceForGroup(g)\n\tassert.NoError(t, err)\n\n\tgc1 := weshnet.NewContextGroup(g, nil, nil, secretStore1, omd1, nil)\n\n\tdeviceChainKey1For1, err := secretStore1.GetShareableChainKey(ctx, g, gc1.MemberPubKey())\n\tassert.NoError(t, err)\n\n\terr = secretStore1.RegisterChainKey(ctx, g, gc1.DevicePubKey(), deviceChainKey1For1)\n\tassert.NoError(t, err)\n\n\tsecretStore2, err := secretstore.NewInMemSecretStore(nil)\n\tassert.NoError(t, err)\n\tassert.NotNil(t, secretStore2)\n\n\tt.Cleanup(func() {\n\t\t_ = secretStore2.Close()\n\t})\n\n\tomd2, err := secretStore2.GetOwnMemberDeviceForGroup(g)\n\tassert.NoError(t, err)\n\n\tpayloadRef1, err := proto.Marshal(&protocoltypes.EncryptedMessage{Plaintext: []byte(\"Test payload 1\")})\n\tassert.NoError(t, err)\n\n\tdeviceChainKey1For2, err := secretStore1.GetShareableChainKey(ctx, g, omd2.Member())\n\tassert.NoError(t, err)\n\n\tdeviceChainKey2For2, err := secretStore2.GetShareableChainKey(ctx, g, omd2.Member())\n\tassert.NoError(t, err)\n\n\terr = secretStore2.RegisterChainKey(ctx, g, omd2.Device(), deviceChainKey2For2)\n\tassert.NoError(t, err)\n\n\terr = secretStore2.RegisterChainKey(ctx, g, omd1.Device(), deviceChainKey1For2)\n\tassert.NoError(t, err)\n\n\tenv1, err := secretStore1.SealEnvelope(ctx, g, payloadRef1)\n\tassert.NoError(t, err)\n\n\theaders, payloadClr1, err := openEnvelope(ctx, t, secretStore2, g, omd2.Device(), env1, cid.Undef)\n\tassert.NoError(t, err)\n\n\tdevRaw, err := omd1.Device().Raw()\n\tassert.Equal(t, headers.DevicePk, devRaw)\n\n\tpayloadClrlBytes, err := proto.Marshal(payloadClr1)\n\tassert.NoError(t, err)\n\tassert.Equal(t, payloadRef1, payloadClrlBytes)\n}\n\nfunc testMessageKeyHolderCatchUp(t *testing.T, expectedNewDevices int, isSlow bool) {\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\tif isSlow {\n\t\ttestutil.FilterSpeed(t, testutil.Slow)\n\t}\n\n\tdir := path.Join(os.TempDir(), fmt.Sprintf(\"%d\", os.Getpid()), \"MessageKeyHolderCatchUp\")\n\tdefer os.RemoveAll(dir)\n\n\tpeers, _, cleanup := weshnet.CreatePeersWithGroupTest(ctx, t, dir, 1, 1)\n\tdefer cleanup()\n\n\tpeer := peers[0]\n\tpeer.GC.ActivateGroupContext(nil)\n\n\tsecretStore1 := peer.SecretStore\n\tms1 := peer.GC.MetadataStore()\n\n\tgroupPublicKey, err := peer.GC.Group().GetPubKey()\n\tassert.NoError(t, err)\n\n\tdevicesPK := make([]crypto.PubKey, expectedNewDevices)\n\n\tfor i := 0; i < expectedNewDevices; i++ {\n\t\tdevicesPK[i] = addDummyMemberInMetadataStore(ctx, t, ms1, peer.GC.Group(), peer.GC.MemberPubKey(), true)\n\t}\n\n\tfor i, devicePublicKey := range devicesPK {\n\t\tselect {\n\t\tcase <-time.After(time.Second):\n\t\t\trequire.FailNow(t, \"timeout while waiting for device secret\")\n\t\tcase <-peer.GC.WaitForDeviceAdded(ctx, devicePublicKey):\n\t\t}\n\n\t\tif !assert.True(t, secretStore1.IsChainKeyKnownForDevice(ctx, groupPublicKey, devicePublicKey)) {\n\t\t\tt.Fatalf(\"failed at iteration %d\", i)\n\t\t}\n\t}\n}\n\nfunc TestMessageKeyHolderCatchUp(t *testing.T) {\n\tfor _, testCase := range []struct {\n\t\texpectedNewDevices int\n\t\tslow               bool\n\t}{\n\t\t{\n\t\t\texpectedNewDevices: 2,\n\t\t\tslow:               false,\n\t\t},\n\t\t{\n\t\t\texpectedNewDevices: 10,\n\t\t\tslow:               true,\n\t\t},\n\t} {\n\t\ttestMessageKeyHolderCatchUp(t, testCase.expectedNewDevices, testCase.slow)\n\t}\n}\n\nfunc testMessageKeyHolderSubscription(t *testing.T, expectedNewDevices int, isSlow bool) {\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\tif isSlow {\n\t\ttestutil.FilterSpeed(t, testutil.Slow)\n\t}\n\n\tdir := path.Join(os.TempDir(), fmt.Sprintf(\"%d\", os.Getpid()), \"MessageKeyHolderSubscription\")\n\tdefer os.RemoveAll(dir)\n\n\tpeers, groupPrivateKey, cleanup := weshnet.CreatePeersWithGroupTest(ctx, t, dir, 1, 1)\n\tdefer cleanup()\n\n\tpeer := peers[0]\n\tpeer.GC.ActivateGroupContext(nil)\n\n\tsecretStore1 := peer.SecretStore\n\tms1 := peer.GC.MetadataStore()\n\n\tdevicesPK := make([]crypto.PubKey, expectedNewDevices)\n\n\tfor i := 0; i < expectedNewDevices; i++ {\n\t\tdevicesPK[i] = addDummyMemberInMetadataStore(ctx, t, ms1, peer.GC.Group(), peer.GC.MemberPubKey(), true)\n\t}\n\n\tfor i, devicePublicKey := range devicesPK {\n\t\tselect {\n\t\tcase <-time.After(time.Second):\n\t\t\trequire.FailNow(t, \"timeout while waiting for device secret\")\n\t\tcase <-peer.GC.WaitForDeviceAdded(ctx, devicePublicKey):\n\t\t}\n\n\t\tif !assert.True(t, secretStore1.IsChainKeyKnownForDevice(ctx, groupPrivateKey.GetPublic(), devicePublicKey)) {\n\t\t\tt.Fatalf(\"failed at iteration %d\", i)\n\t\t}\n\t}\n}\n\nfunc TestMessageKeyHolderSubscription(t *testing.T) {\n\tfor _, testCase := range []struct {\n\t\texpectedNewDevices int\n\t\tslow               bool\n\t}{\n\t\t{\n\t\t\texpectedNewDevices: 2,\n\t\t\tslow:               false,\n\t\t},\n\t\t{\n\t\t\texpectedNewDevices: 10,\n\t\t\tslow:               true,\n\t\t},\n\t} {\n\t\ttestMessageKeyHolderSubscription(t, testCase.expectedNewDevices, testCase.slow)\n\t}\n}\n\n// openEnvelope opens a MessageEnvelope and returns the decrypted message.\n// It performs all the necessary steps to decrypt the message.\nfunc openEnvelope(ctx context.Context, t testing.TB, secretStore secretstore.SecretStore, g *protocoltypes.Group, ownPK crypto.PubKey, data []byte, id cid.Cid) (*protocoltypes.MessageHeaders, *protocoltypes.EncryptedMessage, error) {\n\tt.Helper()\n\n\tassert.NotNil(t, secretStore)\n\tassert.NotNil(t, g)\n\n\tenv, headers, err := secretStore.OpenEnvelopeHeaders(data, g)\n\tassert.NoError(t, err)\n\n\tgPK, err := g.GetPubKey()\n\tassert.NoError(t, err)\n\n\tmsg, err := secretStore.OpenEnvelopePayload(ctx, env, headers, gPK, ownPK, id)\n\tassert.NoError(t, err)\n\n\treturn headers, msg, nil\n}\n"
  },
  {
    "path": "pkg/secretstore/secret_store_test.go",
    "content": "package secretstore\n\nimport (\n\t\"context\"\n\tcrand \"crypto/rand\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/ipfs/go-cid\"\n\t\"github.com/ipfs/go-datastore\"\n\t\"github.com/libp2p/go-libp2p/core/crypto\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"google.golang.org/protobuf/proto\"\n\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n)\n\nfunc Test_PushGroupReferences(t *testing.T) {\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\tg, _, err := protocoltypes.NewGroupMultiMember()\n\trequire.NoError(t, err)\n\n\totherSecretStore, err := newInMemSecretStore(nil)\n\trequire.NoError(t, err)\n\tt.Cleanup(func() {\n\t\t_ = otherSecretStore.Close()\n\t})\n\n\townSecretStore, err := newInMemSecretStore(nil)\n\trequire.NoError(t, err)\n\tt.Cleanup(func() {\n\t\t_ = ownSecretStore.Close()\n\t})\n\n\totherMemberDevice, err := otherSecretStore.GetOwnMemberDeviceForGroup(g)\n\trequire.NoError(t, err)\n\n\totherDevicePK, err := otherMemberDevice.Device().Raw()\n\trequire.NoError(t, err)\n\n\tdeviceChainKey, err := newDeviceChainKey()\n\trequire.NoError(t, err)\n\n\t// test with the deviceChainKey counter\n\tupdateAndTestPushGroupReferences(ctx, ownSecretStore, otherDevicePK, deviceChainKey.Counter, g, t)\n\n\t// do the same test with a new device chain key counter,\n\t// so we can test if old references are deleted\n\tupdateAndTestPushGroupReferences(ctx, ownSecretStore, otherDevicePK, deviceChainKey.Counter+10, g, t)\n}\n\nfunc updateAndTestPushGroupReferences(ctx context.Context, secretStore *secretStore, devicePK []byte, counter uint64, g *protocoltypes.Group, t *testing.T) {\n\t// update the push group references\n\terr := secretStore.UpdateOutOfStoreGroupReferences(ctx, devicePK, counter, g)\n\trequire.NoError(t, err)\n\n\t// test that the push group references are updated\n\t// refs start counter - 100 to counter + 100\n\tstart := counter - PrecomputeOutOfStoreGroupRefsCount\n\tend := counter + PrecomputeOutOfStoreGroupRefsCount\n\n\tfor i := start; i < end; i++ {\n\t\t// compute the push group reference\n\t\tpushGroupRef, err := createOutOfStoreGroupReference(g, devicePK, i)\n\t\trequire.NoError(t, err)\n\n\t\t_, err = secretStore.OutOfStoreGetGroupPublicKeyByGroupReference(ctx, pushGroupRef)\n\t\trequire.NoError(t, err, fmt.Sprintf(\"started at %d, failed as %d\", start, i))\n\t}\n\n\t// test boundary conditions\n\n\t// before the start counter\n\t{\n\t\tbefore := counter - PrecomputeOutOfStoreGroupRefsCount - 1\n\t\tpushGroupRef, err := createOutOfStoreGroupReference(g, devicePK, before)\n\t\trequire.NoError(t, err)\n\t\t_, err = secretStore.OutOfStoreGetGroupPublicKeyByGroupReference(ctx, pushGroupRef)\n\t\trequire.Error(t, err)\n\t}\n\n\t// after the end counter\n\t{\n\t\tend := counter + PrecomputeOutOfStoreGroupRefsCount + 1\n\t\tpushGroupRef, err := createOutOfStoreGroupReference(g, devicePK, end)\n\t\trequire.NoError(t, err)\n\t\t_, err = secretStore.OutOfStoreGetGroupPublicKeyByGroupReference(ctx, pushGroupRef)\n\t\trequire.Error(t, err)\n\t}\n}\n\nfunc Test_SealOutOfStoreMessageEnvelope_OpenOutOfStoreMessage(t *testing.T) {\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\tg, _, err := protocoltypes.NewGroupMultiMember()\n\trequire.NoError(t, err)\n\n\taccUnrelated, err := newInMemSecretStore(nil)\n\trequire.NoError(t, err)\n\trequire.NotNil(t, accUnrelated)\n\tt.Cleanup(func() {\n\t\t_ = accUnrelated.Close()\n\t})\n\n\terr = accUnrelated.PutGroup(ctx, g)\n\trequire.NoError(t, err)\n\n\tacc1, err := newInMemSecretStore(nil)\n\trequire.NoError(t, err)\n\tt.Cleanup(func() {\n\t\t_ = acc1.Close()\n\t})\n\n\tacc2, err := newInMemSecretStore(nil)\n\trequire.NoError(t, err)\n\tt.Cleanup(func() {\n\t\t_ = acc1.Close()\n\t})\n\n\tmemberDevice1ForGroup, err := acc1.GetOwnMemberDeviceForGroup(g)\n\trequire.NoError(t, err)\n\n\tmemberDevice2ForGroup, err := acc2.GetOwnMemberDeviceForGroup(g)\n\trequire.NoError(t, err)\n\n\tdeviceChainKey1For2, err := acc1.GetShareableChainKey(ctx, g, memberDevice2ForGroup.Member())\n\trequire.NoError(t, err)\n\n\ttestPayload := []byte(\"test payload\")\n\n\terr = acc2.RegisterChainKey(ctx, g, memberDevice1ForGroup.Device(), deviceChainKey1For2)\n\trequire.NoError(t, err)\n\n\tenvEncrypted, err := acc1.SealEnvelope(ctx, g, testPayload)\n\trequire.NoError(t, err)\n\n\tenv, headers, err := ((*secretStore)(nil)).OpenEnvelopeHeaders(envEncrypted, g)\n\trequire.NoError(t, err)\n\n\toutOfStoreEnv, err := (*secretStore)(nil).SealOutOfStoreMessageEnvelope(cid.Undef, env, headers, g)\n\trequire.NoError(t, err)\n\n\tgroupPublicKey, err := acc2.OutOfStoreGetGroupPublicKeyByGroupReference(ctx, outOfStoreEnv.GroupReference)\n\trequire.NoError(t, err)\n\n\toutOfStoreMessage, err := accUnrelated.decryptOutOfStoreMessageEnv(ctx, outOfStoreEnv, groupPublicKey)\n\trequire.NoError(t, err)\n\n\tpayload, newlyDecrypted, err := acc2.OutOfStoreMessageOpen(ctx, outOfStoreMessage, groupPublicKey)\n\trequire.NoError(t, err)\n\trequire.True(t, newlyDecrypted)\n\trequire.Equal(t, testPayload, payload)\n}\n\nfunc Test_OutOfStoreDeserialize(t *testing.T) {\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\tsecretStore1, err := NewSecretStore(datastore.NewMapDatastore(), nil)\n\trequire.NoError(t, err)\n\n\tt.Cleanup(func() { _ = secretStore1.Close() })\n\n\tsecretStore2, err := NewSecretStore(datastore.NewMapDatastore(), nil)\n\trequire.NoError(t, err)\n\n\tt.Cleanup(func() { _ = secretStore2.Close() })\n\n\tgroup, _, err := protocoltypes.NewGroupMultiMember()\n\trequire.NoError(t, err)\n\n\trequire.NoError(t, secretStore1.PutGroup(ctx, group))\n\trequire.NoError(t, secretStore2.PutGroup(ctx, group))\n\n\tmemberDevice1, err := secretStore1.GetOwnMemberDeviceForGroup(group)\n\trequire.NoError(t, err)\n\n\tmemberDevice2, err := secretStore2.GetOwnMemberDeviceForGroup(group)\n\trequire.NoError(t, err)\n\n\tchainKey1For2, err := secretStore1.GetShareableChainKey(ctx, group, memberDevice2.Member())\n\trequire.NoError(t, err)\n\n\tchainKey2For1, err := secretStore2.GetShareableChainKey(ctx, group, memberDevice1.Member())\n\trequire.NoError(t, err)\n\n\trequire.NoError(t, secretStore1.RegisterChainKey(ctx, group, memberDevice2.Device(), chainKey2For1))\n\trequire.NoError(t, secretStore2.RegisterChainKey(ctx, group, memberDevice1.Device(), chainKey1For2))\n\n\ttestPayload := []byte(\"test payload\")\n\n\tenv, err := secretStore1.SealEnvelope(ctx, group, testPayload)\n\trequire.NoError(t, err)\n\n\tenvHeaders, msgHeaders, err := secretStore1.OpenEnvelopeHeaders(env, group)\n\trequire.NoError(t, err)\n\n\tdummyCID, err := cid.Parse(\"QmNR2n4zywCV61MeMLB6JwPueAPqheqpfiA4fLPMxouEmQ\")\n\trequire.NoError(t, err)\n\n\tdevice1Raw, err := memberDevice1.Device().Raw()\n\trequire.NoError(t, err)\n\n\toutOfStoreMessageEnvelope, err := secretStore1.SealOutOfStoreMessageEnvelope(dummyCID, envHeaders, msgHeaders, group)\n\trequire.NoError(t, err)\n\n\toutOfStoreMessageEnvelopeBytes, err := proto.Marshal(outOfStoreMessageEnvelope)\n\trequire.NoError(t, err)\n\n\t// Attempting to decrypt the message without a relay\n\t{\n\t\topenedOutOfStoreMessage, groupFound, clearPayload, alreadyDecrypted, err := secretStore2.OpenOutOfStoreMessage(ctx, outOfStoreMessageEnvelopeBytes)\n\t\trequire.NoError(t, err)\n\n\t\trequire.Equal(t, testPayload, clearPayload)\n\t\trequire.Equal(t, device1Raw, openedOutOfStoreMessage.DevicePk)\n\t\trequire.Equal(t, dummyCID.Bytes(), openedOutOfStoreMessage.Cid)\n\t\trequire.Equal(t, group.PublicKey, groupFound.PublicKey)\n\t\trequire.False(t, alreadyDecrypted)\n\t}\n}\n\nfunc Test_EncryptMessageEnvelopeAndDerive(t *testing.T) {\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\tg, _, err := protocoltypes.NewGroupMultiMember()\n\tassert.NoError(t, err)\n\n\tmkh1, err := newInMemSecretStore(nil)\n\tassert.NoError(t, err)\n\tt.Cleanup(func() {\n\t\t_ = mkh1.Close()\n\t})\n\n\tmkh2, err := newInMemSecretStore(nil)\n\tassert.NoError(t, err)\n\tt.Cleanup(func() {\n\t\t_ = mkh1.Close()\n\t})\n\n\tomd1, err := mkh1.GetOwnMemberDeviceForGroup(g)\n\tassert.NoError(t, err)\n\n\tomd2, err := mkh2.GetOwnMemberDeviceForGroup(g)\n\tassert.NoError(t, err)\n\n\tgPK, err := g.GetPubKey()\n\tassert.NoError(t, err)\n\n\tgc1DevicePubKey := omd1.Device()\n\n\tds1For1Encrypted, err := mkh1.GetShareableChainKey(ctx, g, omd1.Member())\n\tassert.NoError(t, err)\n\n\tds1For2Encrypted, err := mkh1.GetShareableChainKey(ctx, g, omd2.Member())\n\tassert.NoError(t, err)\n\n\tds2For2Encrypted, err := mkh2.GetShareableChainKey(ctx, g, omd2.Member())\n\tassert.NoError(t, err)\n\n\terr = mkh1.RegisterChainKey(ctx, g, omd1.Device(), ds1For1Encrypted)\n\tassert.NoError(t, err)\n\n\terr = mkh2.RegisterChainKey(ctx, g, omd2.Device(), ds2For2Encrypted)\n\tassert.NoError(t, err)\n\n\terr = mkh2.RegisterChainKey(ctx, g, omd1.Device(), ds1For2Encrypted)\n\tassert.NoError(t, err)\n\n\tds1, err := mkh1.getOwnDeviceChainKeyForGroup(ctx, g)\n\tassert.NoError(t, err)\n\n\tinitialCounter := ds1.Counter\n\n\tfor i := 0; i < 1000; i++ {\n\t\tpayloadRef, err := proto.Marshal(&protocoltypes.EncryptedMessage{Plaintext: []byte(\"Test payload 1\")})\n\t\tassert.NoError(t, err)\n\t\tenvEncrypted, err := mkh1.SealEnvelope(ctx, g, payloadRef)\n\t\tassert.NoError(t, err)\n\n\t\tds, err := mkh1.getDeviceChainKeyForGroupAndDevice(ctx, gPK, gc1DevicePubKey)\n\t\tif !assert.NoError(t, err) {\n\t\t\tt.Fatalf(\"failed at i = %d\", i)\n\t\t}\n\t\tassert.Equal(t, ds.Counter, initialCounter+uint64(i+1))\n\n\t\tenv, headers, err := ((*secretStore)(nil)).OpenEnvelopeHeaders(envEncrypted, g)\n\t\tif !assert.NoError(t, err) {\n\t\t\tt.Fatalf(\"failed at i = %d\", i)\n\t\t}\n\n\t\tpayloadClr, err := mkh2.OpenEnvelopePayload(ctx, env, headers, gPK, omd2.Device(), cid.Undef)\n\t\tif !assert.NoError(t, err) {\n\t\t\tt.Fatalf(\"failed at i = %d\", i)\n\t\t}\n\n\t\tif assert.NotNil(t, headers) && assert.NotNil(t, payloadClr) {\n\t\t\tdevRaw, err := omd1.Device().Raw()\n\t\t\tassert.NoError(t, err)\n\n\t\t\tassert.Equal(t, headers.DevicePk, devRaw)\n\n\t\t\tpayloadClrBytes, err := proto.Marshal(payloadClr)\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.Equal(t, payloadRef, payloadClrBytes)\n\t\t} else {\n\t\t\tbreak\n\t\t}\n\t}\n}\n\nfunc mustDeviceChainKey(t testing.TB) func(ds *protocoltypes.DeviceChainKey, err error) *protocoltypes.DeviceChainKey {\n\treturn func(ds *protocoltypes.DeviceChainKey, err error) *protocoltypes.DeviceChainKey {\n\t\tt.Helper()\n\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\treturn ds\n\t}\n}\n\nfunc mustMessageHeaders(t testing.TB, omd OwnMemberDevice, counter uint64, payload []byte) *protocoltypes.MessageHeaders {\n\tt.Helper()\n\n\tpkB, err := omd.Device().Raw()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tsig, err := omd.DeviceSign(payload)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\treturn &protocoltypes.MessageHeaders{\n\t\tCounter:  counter,\n\t\tDevicePk: pkB,\n\t\tSig:      sig,\n\t}\n}\n\nfunc Test_EncryptMessagePayload(t *testing.T) {\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\tgroup, _, err := protocoltypes.NewGroupMultiMember()\n\tassert.NoError(t, err)\n\n\tmkh1, err := newInMemSecretStore(nil)\n\tassert.NoError(t, err)\n\tt.Cleanup(func() {\n\t\tmkh1.Close()\n\t})\n\n\tmkh2, err := newInMemSecretStore(nil)\n\tassert.NoError(t, err)\n\tt.Cleanup(func() {\n\t\tmkh2.Close()\n\t})\n\n\tomd1, err := mkh1.GetOwnMemberDeviceForGroup(group)\n\tassert.NoError(t, err)\n\n\tomd2, err := mkh2.GetOwnMemberDeviceForGroup(group)\n\tassert.NoError(t, err)\n\n\tencryptedDS1For1, err := mkh1.GetShareableChainKey(ctx, group, omd1.Member())\n\tassert.NoError(t, err)\n\n\tencryptedDS1For2, err := mkh1.GetShareableChainKey(ctx, group, omd2.Member())\n\tassert.NoError(t, err)\n\n\tencryptedDS2For2, err := mkh2.GetShareableChainKey(ctx, group, omd2.Member())\n\tassert.NoError(t, err)\n\n\tgc1DevicePubKey := omd1.Device()\n\tgc2DevicePubKey := omd2.Device()\n\n\terr = mkh1.RegisterChainKey(ctx, group, gc1DevicePubKey, encryptedDS1For1)\n\tassert.NoError(t, err)\n\n\terr = mkh2.RegisterChainKey(ctx, group, gc2DevicePubKey, encryptedDS2For2)\n\tassert.NoError(t, err)\n\n\tds1, err := mkh1.getOwnDeviceChainKeyForGroup(ctx, group)\n\tassert.NoError(t, err)\n\n\tinitialCounter := ds1.Counter\n\tfirstDeviceChainKey := append([]byte(nil), ds1.ChainKey...)\n\n\tpayloadRef1 := []byte(\"ok, this is the first test\")\n\tpayloadRef2 := []byte(\"so, this is a second test\")\n\tpayloadRef3 := []byte(\"this will be posted many times\")\n\n\terr = mkh2.RegisterChainKey(ctx, group, omd1.Device(), encryptedDS1For2)\n\tassert.NoError(t, err)\n\n\tgPK, err := group.GetPubKey()\n\tassert.NoError(t, err)\n\n\tassert.Equal(t, mustDeviceChainKey(t)(mkh1.getDeviceChainKeyForGroupAndDevice(ctx, gPK, omd1.Device())).ChainKey, firstDeviceChainKey)\n\n\tpayloadEnc1, _, err := sealPayload(payloadRef1, mustDeviceChainKey(t)(mkh1.getDeviceChainKeyForGroupAndDevice(ctx, gPK, omd1.Device())), omd1.(*ownMemberDevice).device, group)\n\tassert.NoError(t, err)\n\n\t// secret is derived by sealEnvelope\n\terr = mkh1.deriveDeviceChainKey(ctx, group, omd1.Device())\n\tassert.NoError(t, err)\n\n\tassert.NotEqual(t, hex.EncodeToString(payloadRef1), hex.EncodeToString(payloadEnc1))\n\n\t// Messages are encrypted with DeviceChainKey.Counter\n\t// uint64 overflows to 0, which is the expected behaviour\n\n\t// Test with a wrong counter value\n\tpayloadClr1, decryptInfo, err := mkh2.openPayload(ctx, cid.Undef, gPK, payloadEnc1, mustMessageHeaders(t, omd1, initialCounter+2, payloadRef1))\n\tassert.Error(t, err)\n\tassert.Nil(t, decryptInfo)\n\tassert.Equal(t, \"\", string(payloadClr1))\n\n\t// Test with a valid counter value, but no CID (so no cache)\n\tpayloadClr1, decryptInfo, err = mkh2.openPayload(ctx, cid.Undef, gPK, payloadEnc1, mustMessageHeaders(t, omd1, initialCounter+1, payloadRef1))\n\tassert.NoError(t, err)\n\tassert.Equal(t, string(payloadRef1), string(payloadClr1))\n\n\terr = mkh2.postDecryptActions(ctx, decryptInfo, gPK, omd2.Device(), mustMessageHeaders(t, omd1, initialCounter+1, payloadRef1))\n\tassert.NoError(t, err)\n\n\tds, err := mkh1.getDeviceChainKeyForGroupAndDevice(ctx, gPK, omd1.Device())\n\tassert.NoError(t, err)\n\n\tassert.Equal(t, ds.Counter, initialCounter+1)\n\tassert.NotEqual(t, ds.ChainKey, firstDeviceChainKey)\n\n\tpayloadEnc2, _, err := sealPayload(payloadRef1, ds, omd1.(*ownMemberDevice).device, group)\n\tassert.NoError(t, err)\n\n\terr = mkh1.deriveDeviceChainKey(ctx, group, omd1.Device())\n\tassert.NoError(t, err)\n\n\t// Ensure that encrypted message is not the same as the first message\n\tassert.NotEqual(t, hex.EncodeToString(payloadRef1), hex.EncodeToString(payloadEnc2))\n\tassert.NotEqual(t, hex.EncodeToString(payloadEnc1), hex.EncodeToString(payloadEnc2))\n\n\tpayloadClr2, decryptInfo, err := mkh2.openPayload(ctx, cid.Undef, gPK, payloadEnc2, mustMessageHeaders(t, omd1, initialCounter+2, payloadRef1))\n\tassert.NoError(t, err)\n\n\terr = mkh2.postDecryptActions(ctx, decryptInfo, gPK, omd2.Device(), mustMessageHeaders(t, omd1, initialCounter+2, payloadRef1))\n\tassert.NoError(t, err)\n\n\tassert.Equal(t, string(payloadRef1), string(payloadClr2))\n\n\t// Make sure that a message without a CID can't be decrypted twice\n\tpayloadClr2, decryptInfo, err = mkh2.openPayload(ctx, cid.Undef, gPK, payloadEnc2, mustMessageHeaders(t, omd1, initialCounter+1, payloadRef1))\n\tassert.Error(t, err)\n\tassert.Equal(t, \"\", string(payloadClr2))\n\n\tds, err = mkh1.getDeviceChainKeyForGroupAndDevice(ctx, gPK, omd1.Device())\n\tassert.NoError(t, err)\n\n\t// Make sure that a message a CID can be decrypted twice\n\tpayloadEnc3, _, err := sealPayload(payloadRef2, ds, omd1.(*ownMemberDevice).device, group)\n\tassert.NoError(t, err)\n\n\terr = mkh1.deriveDeviceChainKey(ctx, group, omd1.Device())\n\tassert.NoError(t, err)\n\n\tdummyCID1, err := cid.Parse(\"QmbdQXQh9B2bWZgZJqfbjNPV5jGN2owbQ3vjeYsaDaCDqU\")\n\tassert.NoError(t, err)\n\n\tdummyCID2, err := cid.Parse(\"Qmf8oj9wbfu73prNAA1cRQVDqA52gD5B3ApnYQQjcjffH4\")\n\tassert.NoError(t, err)\n\n\t// Not decrypted message yet, wrong counter value\n\tpayloadClr3, decryptInfo, err := mkh2.openPayload(ctx, dummyCID1, gPK, payloadEnc3, mustMessageHeaders(t, omd1, initialCounter+2, payloadRef2))\n\tassert.Error(t, err)\n\tassert.Equal(t, \"\", string(payloadClr3))\n\n\tpayloadClr3, decryptInfo, err = mkh2.openPayload(ctx, dummyCID1, gPK, payloadEnc3, mustMessageHeaders(t, omd1, initialCounter+3, payloadRef2))\n\tassert.NoError(t, err)\n\tassert.Equal(t, string(payloadRef2), string(payloadClr3))\n\n\terr = mkh2.postDecryptActions(ctx, decryptInfo, gPK, omd2.Device(), mustMessageHeaders(t, omd1, initialCounter+3, payloadRef2))\n\tassert.NoError(t, err)\n\n\tpayloadClr3, decryptInfo, err = mkh2.openPayload(ctx, dummyCID1, gPK, payloadEnc3, mustMessageHeaders(t, omd1, initialCounter+3, payloadRef2))\n\tassert.NoError(t, err)\n\tassert.Equal(t, string(payloadRef2), string(payloadClr3))\n\n\terr = mkh2.postDecryptActions(ctx, decryptInfo, gPK, omd2.Device(), mustMessageHeaders(t, omd1, initialCounter+3, payloadRef2))\n\tassert.NoError(t, err)\n\n\t// Wrong CID\n\tpayloadClr3, decryptInfo, err = mkh2.openPayload(ctx, dummyCID2, gPK, payloadEnc3, mustMessageHeaders(t, omd1, initialCounter+3, payloadRef2))\n\tassert.Error(t, err)\n\tassert.Equal(t, \"\", string(payloadClr3))\n\n\t// Reused CID, wrong counter value\n\tpayloadClr3, decryptInfo, err = mkh2.openPayload(ctx, dummyCID1, gPK, payloadEnc3, mustMessageHeaders(t, omd1, initialCounter+4, payloadRef2))\n\tassert.Error(t, err)\n\tassert.Equal(t, \"\", string(payloadClr3))\n\n\tmassExpected := uint64(200)\n\n\t// Test appending 200 messages, to ensure new secrets are generated correctly\n\tfor i := uint64(0); i < massExpected; i++ {\n\t\tds, err = mkh1.getDeviceChainKeyForGroupAndDevice(ctx, gPK, omd1.Device())\n\t\tassert.NoError(t, err)\n\n\t\tpayloadEnc, _, err := sealPayload(payloadRef3, ds, omd1.(*ownMemberDevice).device, group)\n\t\tassert.NoError(t, err)\n\n\t\terr = mkh1.deriveDeviceChainKey(ctx, group, omd1.Device())\n\t\tassert.NoError(t, err)\n\n\t\tds, err = mkh1.getDeviceChainKeyForGroupAndDevice(ctx, gPK, omd1.Device())\n\t\tassert.NoError(t, err)\n\n\t\tcounter := ds.Counter\n\n\t\tpayloadClr, decryptInfo, err := mkh2.openPayload(ctx, cid.Undef, gPK, payloadEnc, mustMessageHeaders(t, omd1, counter, payloadRef3))\n\t\tif !assert.NoError(t, err) {\n\t\t\tt.Fatalf(\"failed at i = %d\", i)\n\t\t}\n\n\t\terr = mkh2.postDecryptActions(ctx, decryptInfo, gPK, omd2.Device(), mustMessageHeaders(t, omd1, counter, payloadRef3))\n\t\tassert.NoError(t, err)\n\n\t\tassert.Equal(t, string(payloadRef3), string(payloadClr))\n\t}\n\n\tds, err = mkh1.getDeviceChainKeyForGroupAndDevice(ctx, gPK, omd1.Device())\n\tassert.NoError(t, err)\n\n\tassert.Equal(t, initialCounter+massExpected+3, ds.Counter)\n}\n\nfunc TestGetGroupForContact(t *testing.T) {\n\tprivateKey, _, err := crypto.GenerateEd25519Key(crand.Reader)\n\trequire.NoError(t, err)\n\n\tgroup, err := getGroupForContact(privateKey)\n\trequire.NoError(t, err)\n\n\trequire.Equal(t, group.GroupType, protocoltypes.GroupType_GroupTypeContact)\n\trequire.Equal(t, len(group.PublicKey), 32)\n\trequire.Equal(t, len(group.Secret), 32)\n}\n\nfunc TestGetKeysForGroupOfContact(t *testing.T) {\n\tprivateKey, _, err := crypto.GenerateEd25519Key(crand.Reader)\n\trequire.NoError(t, err)\n\n\tgroupPrivateKey, groupSecretPrivateKey, err := getKeysForGroupOfContact(privateKey)\n\trequire.NoError(t, err)\n\n\trequire.NotNil(t, groupPrivateKey)\n\trequire.NotNil(t, groupSecretPrivateKey)\n\trequire.False(t, groupPrivateKey.Equals(groupSecretPrivateKey))\n}\n"
  },
  {
    "path": "pkg/testutil/doc.go",
    "content": "// Package testutil contains testing helpers (logging, slow skipping).\npackage testutil\n"
  },
  {
    "path": "pkg/testutil/example_test.go",
    "content": "package testutil_test\n"
  },
  {
    "path": "pkg/testutil/filters.go",
    "content": "package testutil\n\nimport (\n\t\"testing\"\n\n\t\"google.golang.org/protobuf/proto\"\n\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n)\n\nfunc TestFilterGroupMetadataPayloadSent(t *testing.T, events <-chan *protocoltypes.GroupMetadataEvent) []*protocoltypes.GroupMetadataPayloadSent {\n\tt.Helper()\n\n\tout := []*protocoltypes.GroupMetadataPayloadSent(nil)\n\n\tfor evt := range events {\n\t\tif evt == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tif evt.Metadata.EventType != protocoltypes.EventType_EventTypeGroupMetadataPayloadSent {\n\t\t\tcontinue\n\t\t}\n\n\t\tm := &protocoltypes.GroupMetadataPayloadSent{}\n\t\tif err := proto.Unmarshal(evt.Event, m); err != nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tout = append(out, m)\n\t}\n\n\treturn out\n}\n"
  },
  {
    "path": "pkg/testutil/logging.go",
    "content": "package testutil\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"go.uber.org/zap\"\n\t\"moul.io/zapring\"\n\n\t\"berty.tech/weshnet/v2/pkg/logutil\"\n)\n\nconst defaultLoggingFilters = \"info+:bty.test* error+:*,-ipfs*,-*.tyber\"\n\nvar (\n\tlogFilters     = flag.String(\"log-filters\", defaultLoggingFilters, \"log namespaces\")\n\tlogFile        = flag.String(\"log-file\", \"\", \"log to file\")\n\tlogFormat      = flag.String(\"log-format\", \"color\", \"json, console, color\")\n\tloggerInstance *zap.Logger\n\tloggerCleanup  func()\n\tloggerInitOnce sync.Once\n\tloggerRing     *zapring.Core\n)\n\nfunc Logger(t testing.TB) (*zap.Logger, func()) {\n\tt.Helper()\n\n\tloggerInstance, _, loggerCleanup := LoggerWithRing(t)\n\treturn loggerInstance, loggerCleanup\n}\n\nfunc LoggerWithRing(t testing.TB) (*zap.Logger, *zapring.Core, func()) {\n\tt.Helper()\n\n\tloggerInitOnce.Do(func() {\n\t\tif val := os.Getenv(\"BERTY_LOGFILTERS\"); val != \"\" {\n\t\t\t*logFilters = val\n\t\t}\n\t\tif val := os.Getenv(\"BERTY_LOGFILE\"); val != \"\" {\n\t\t\t*logFile = val\n\t\t}\n\t\tif val := os.Getenv(\"BERTY_LOGFORMAT\"); val != \"\" {\n\t\t\t*logFormat = val\n\t\t}\n\t\t*logFilters = strings.ReplaceAll(*logFilters, \":default:\", defaultLoggingFilters)\n\n\t\tvar err error\n\t\tloggerRing = zapring.New(10 * 1024 * 1024)\n\t\tloggerInstance, loggerCleanup, err = logutil.NewLogger(\n\t\t\tlogutil.NewStdStream(*logFilters, *logFormat, *logFile),\n\t\t\tlogutil.NewRingStream(*logFilters, *logFormat, loggerRing),\n\t\t)\n\t\tif !assert.NoError(t, err) {\n\t\t\tloggerInstance = zap.NewNop()\n\t\t\tloggerCleanup = func() {}\n\t\t}\n\t})\n\n\treturn loggerInstance, loggerRing, loggerCleanup\n}\n\nfunc LogTree(t *testing.T, log string, indent int, title bool, args ...any) {\n\tt.Helper()\n\tif os.Getenv(\"SHOW_LOG_TREES\") != \"1\" {\n\t\treturn\n\t}\n\n\tif len(args) > 0 {\n\t\tlog = fmt.Sprintf(log, args...)\n\t}\n\n\tif !title {\n\t\tlog = \"└── \" + log\n\t}\n\n\tfor i := 0; i < indent; i++ {\n\t\tlog = \"│  \" + log\n\t}\n\n\tt.Log(log)\n}\n"
  },
  {
    "path": "pkg/testutil/require.go",
    "content": "package testutil\n\nimport (\n\t\"io\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc Close(t *testing.T, closer io.Closer) {\n\tt.Helper()\n\trequire.NoError(t, closer.Close())\n}\n"
  },
  {
    "path": "pkg/testutil/skip.go",
    "content": "package testutil\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\t\"testing\"\n)\n\n// Stability level enum\ntype Stability string\n\nconst (\n\tStable       Stability = \"stable\"\n\tFlappy       Stability = \"flappy\"\n\tBroken       Stability = \"broken\"\n\tAnyStability Stability = \"any\"\n)\n\n// Speed level enum\ntype Speed string\n\nconst (\n\tFast     Speed = \"fast\"\n\tSlow     Speed = \"slow\"\n\tAnySpeed Speed = \"any\"\n)\n\n// RacePolicy enum\ntype RacePolicy string\n\nconst (\n\tSkipIfRace RacePolicy = \"skip-if-race\"\n\tRunIfRace  RacePolicy = \"run-if-race\"\n)\n\n// Default levels\nconst (\n\tdefaultStabilityFilter Stability = Stable\n\tdefaultSpeedFilter     Speed     = AnySpeed\n)\n\nvar (\n\tenabledStability = map[Stability]bool{}\n\tenabledSpeed     = map[Speed]bool{}\n\tenvParsed        = false\n)\n\nfunc parseEnv() {\n\t// Get stability filters\n\tstabFilter := os.Getenv(\"TEST_STABILITY\")\n\tif stabFilter == \"\" {\n\t\tstabFilter = string(defaultStabilityFilter)\n\t}\n\n\tfor _, level := range strings.Split(stabFilter, \",\") {\n\t\tswitch Stability(level) {\n\t\tcase Stable, Flappy, Broken:\n\t\t\tenabledStability[Stability(level)] = true\n\t\tcase AnyStability:\n\t\t\tenabledStability[Stable] = true\n\t\t\tenabledStability[Flappy] = true\n\t\t\tenabledStability[Broken] = true\n\t\tdefault:\n\t\t\tpanic(fmt.Sprintf(\"invalid stability level: %q\", level))\n\t\t}\n\t}\n\n\t// Get speed filters\n\tspeedFilter := os.Getenv(\"TEST_SPEED\")\n\tif speedFilter == \"\" {\n\t\tspeedFilter = string(defaultSpeedFilter)\n\t}\n\n\tfor _, level := range strings.Split(speedFilter, \",\") {\n\t\tswitch Speed(level) {\n\t\tcase Slow, Fast:\n\t\t\tenabledSpeed[Speed(level)] = true\n\t\tcase AnySpeed:\n\t\t\tenabledSpeed[Slow] = true\n\t\t\tenabledSpeed[Fast] = true\n\t\tdefault:\n\t\t\tpanic(fmt.Sprintf(\"invalid speed level: %q\", level))\n\t\t}\n\t}\n\n\tenvParsed = true\n}\n\nfunc FilterStability(t *testing.T, stability Stability) {\n\tt.Helper()\n\n\tif !envParsed {\n\t\tparseEnv()\n\t}\n\n\tif !enabledStability[stability] {\n\t\tt.Skipf(\"skip test with %q stability\", stability)\n\t}\n}\n\nfunc FilterSpeed(t *testing.T, speed Speed) {\n\tt.Helper()\n\n\tif !envParsed {\n\t\tparseEnv()\n\t}\n\n\tif !enabledSpeed[speed] {\n\t\tt.Skipf(\"skip test with %q speed\", speed)\n\t}\n}\n\nfunc FilterStabilityAndSpeed(t *testing.T, stability Stability, speed Speed) {\n\tFilterStability(t, stability)\n\tFilterSpeed(t, speed)\n}\n"
  },
  {
    "path": "pkg/testutil/skip_norace.go",
    "content": "//go:build !race\n\npackage testutil\n\nimport \"testing\"\n\nfunc FilterRace(t *testing.T, race RacePolicy) {\n\tt.Helper()\n\tif race == RunIfRace {\n\t\tt.Skip(\"skipping, this test can only run with the '-race' flag\")\n\t}\n}\n"
  },
  {
    "path": "pkg/testutil/skip_race.go",
    "content": "//go:build race\n\npackage testutil\n\nimport \"testing\"\n\nfunc FilterRace(t *testing.T, race RacePolicy) {\n\tt.Helper()\n\tif race == SkipIfRace {\n\t\tt.Skip(\"skipping, this test can only run without the '-race' flag\")\n\t}\n}\n"
  },
  {
    "path": "pkg/testutil/skip_test.go",
    "content": "package testutil_test\n\nimport (\n\t\"testing\"\n\n\t\"berty.tech/weshnet/v2/pkg/testutil\"\n)\n\nfunc TestFilterRace(t *testing.T) {\n\tt.Run(\"always-run\", func(t *testing.T) {})\n\tt.Run(\"skip-if-race\", func(t *testing.T) {\n\t\ttestutil.FilterRace(t, testutil.SkipIfRace)\n\t})\n\tt.Run(\"run-if-race\", func(t *testing.T) {\n\t\ttestutil.FilterRace(t, testutil.RunIfRace)\n\t})\n\tt.Run(\"always-skip\", func(t *testing.T) {\n\t\tt.Skip()\n\t})\n}\n"
  },
  {
    "path": "pkg/tinder/driver.go",
    "content": "package tinder\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/libp2p/go-libp2p/core/discovery\"\n\t\"github.com/libp2p/go-libp2p/core/peer\"\n)\n\nvar ErrNotSupported = fmt.Errorf(\"not supported\")\n\nvar _ discovery.Discovery = (IDriver)(nil)\n\ntype IDriver interface {\n\tName() string\n\tSubscribe(ctx context.Context, topic string, opts ...discovery.Option) (<-chan peer.AddrInfo, error)\n\tUnregister(ctx context.Context, topic string, opts ...discovery.Option) error\n\n\t// discovery\n\tAdvertise(ctx context.Context, ns string, opts ...discovery.Option) (time.Duration, error)\n\tFindPeers(ctx context.Context, ns string, opts ...discovery.Option) (<-chan peer.AddrInfo, error)\n}\n"
  },
  {
    "path": "pkg/tinder/driver_discovery.go",
    "content": "package tinder\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/libp2p/go-libp2p/core/discovery\"\n\t\"github.com/libp2p/go-libp2p/core/peer\"\n\t\"github.com/libp2p/go-libp2p/core/routing\"\n\tdisc_routing \"github.com/libp2p/go-libp2p/p2p/discovery/routing\"\n)\n\ntype DiscoveryDriver struct {\n\tname string\n\tdiscovery.Discovery\n}\n\nfunc NewRoutingDiscoveryDriver(name string, routing routing.Routing) IDriver {\n\tdisc := disc_routing.NewRoutingDiscovery(routing)\n\treturn NewDiscoveryDriver(name, disc)\n}\n\nfunc NewDiscoveryDriver(name string, disc discovery.Discovery) IDriver {\n\treturn &DiscoveryDriver{name, disc}\n}\n\n// discovery advertise\nfunc (d *DiscoveryDriver) Advertise(ctx context.Context, topic string, opts ...discovery.Option) (time.Duration, error) {\n\treturn d.Discovery.Advertise(ctx, topic, opts...)\n}\n\n// discovery find peers\nfunc (d *DiscoveryDriver) FindPeers(ctx context.Context, topic string, opts ...discovery.Option) (<-chan peer.AddrInfo, error) {\n\treturn d.Discovery.FindPeers(ctx, topic, opts...)\n}\n\nfunc (d *DiscoveryDriver) Subscribe(_ context.Context, _ string, _ ...discovery.Option) (<-chan peer.AddrInfo, error) {\n\treturn nil, ErrNotSupported\n}\n\nfunc (d *DiscoveryDriver) Unregister(_ context.Context, _ string, _ ...discovery.Option) error {\n\treturn ErrNotSupported\n}\n\nfunc (d *DiscoveryDriver) Name() string {\n\treturn d.name\n}\n"
  },
  {
    "path": "pkg/tinder/driver_localdiscovery.go",
    "content": "package tinder\n\nimport (\n\t\"container/list\"\n\t\"context\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"math/rand\"\n\t\"slices\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/libp2p/go-libp2p/core/discovery\"\n\t\"github.com/libp2p/go-libp2p/core/event\"\n\t\"github.com/libp2p/go-libp2p/core/host\"\n\t\"github.com/libp2p/go-libp2p/core/network\"\n\t\"github.com/libp2p/go-libp2p/core/peer\"\n\tprotocol \"github.com/libp2p/go-libp2p/core/protocol\"\n\t\"github.com/libp2p/go-libp2p/p2p/host/eventbus\"\n\tma \"github.com/multiformats/go-multiaddr\"\n\tmanet \"github.com/multiformats/go-multiaddr/net\"\n\t\"go.uber.org/zap\"\n\n\tnearby \"berty.tech/weshnet/v2/pkg/androidnearby\"\n\tble \"berty.tech/weshnet/v2/pkg/ble-driver\"\n\t\"berty.tech/weshnet/v2/pkg/logutil\"\n\tmc \"berty.tech/weshnet/v2/pkg/multipeer-connectivity-driver\"\n\t\"berty.tech/weshnet/v2/pkg/protoio\"\n)\n\nconst (\n\t// LocalDiscoveryName is the name of the localdiscovery driver\n\tLocalDiscoveryName = \"localdisc\"\n\n\trecProtocolID = protocol.ID(\"wesh/p2p/localrecord\")\n\n\tminTTL   = 7200 * time.Second\n\tmaxLimit = 1000\n)\n\ntype LocalDiscovery struct {\n\trootctx    context.Context\n\trootcancel context.CancelFunc\n\n\th      host.Host\n\tlogger *zap.Logger\n\n\trecs   map[string] /* topic */ time.Time\n\tmuRecs sync.RWMutex\n\n\tcaches   map[string] /* topic */ *linkedCache\n\tmuCaches sync.RWMutex\n}\n\ntype linkedCache struct {\n\tsync.Locker\n\trecs  map[peer.ID]*recCache\n\tqueue *list.List\n\tcond  *sync.Cond\n}\n\ntype recCache struct {\n\ttopic  string\n\tpeer   *peer.AddrInfo\n\texpire time.Time\n}\n\nfunc newLinkedCache() *linkedCache {\n\tlocker := sync.Mutex{}\n\treturn &linkedCache{\n\t\tqueue:  list.New(),\n\t\trecs:   make(map[peer.ID]*recCache),\n\t\tLocker: &locker,\n\t\tcond:   sync.NewCond(&locker),\n\t}\n}\n\nfunc NewLocalDiscovery(logger *zap.Logger, host host.Host, _ *rand.Rand) (*LocalDiscovery, error) {\n\tctx, cancel := context.WithCancel(context.Background())\n\tld := &LocalDiscovery{\n\t\trootctx:    ctx,\n\t\trootcancel: cancel,\n\n\t\tlogger: logger.Named(\"localdisc\"),\n\t\th:      host,\n\t\trecs:   make(map[string]time.Time),\n\t\tcaches: make(map[string]*linkedCache),\n\t}\n\n\thost.SetStreamHandler(recProtocolID, ld.handleStream)\n\tif err := ld.monitorConnection(ctx); err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to monitor connection: %w\", err)\n\t}\n\n\treturn ld, nil\n}\n\nfunc (ld *LocalDiscovery) Close() error {\n\tld.rootcancel()\n\treturn nil\n}\n\nfunc (ld *LocalDiscovery) Advertise(ctx context.Context, cid string, opts ...discovery.Option) (time.Duration, error) {\n\tld.logger.Debug(\"advertise record\", logutil.PrivateString(\"ns\", cid))\n\n\t// Get options\n\tvar options discovery.Options\n\terr := options.Apply(opts...)\n\tif err != nil {\n\t\treturn minTTL, err\n\t}\n\n\tlimit := options.Limit\n\tif limit == 0 || limit > maxLimit {\n\t\tlimit = maxLimit\n\t}\n\n\tttl := max(options.Ttl, minTTL)\n\n\texpire := time.Now().Add(ttl)\n\n\tld.muRecs.Lock()\n\t_, exist := ld.recs[cid]\n\tif !exist && len(ld.recs) > limit {\n\t\tld.muRecs.Unlock()\n\t\treturn minTTL, fmt.Errorf(\"unable to add record, reached limit of %d\", limit)\n\t}\n\n\tld.recs[cid] = expire\n\tld.muRecs.Unlock()\n\n\tld.logger.Debug(\"advertise\", zap.String(\"ns\", hex.EncodeToString([]byte(cid))))\n\n\t// send it to already connected proximity peers\n\trecords := []*Record{{Cid: cid, Expire: expire.Unix()}}\n\tld.sendRecordsToProximityPeers(ctx, &Records{Records: records})\n\n\treturn ttl, nil\n}\n\nfunc (ld *LocalDiscovery) FindPeers(_ context.Context, cid string, opts ...discovery.Option) (<-chan peer.AddrInfo, error) {\n\t// Get options\n\tvar options discovery.Options\n\terr := options.Apply(opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tlimit := options.Limit\n\tif limit == 0 || limit > maxLimit {\n\t\tlimit = maxLimit\n\t}\n\n\tld.muCaches.RLock()\n\tdefer ld.muCaches.RUnlock()\n\n\tvar out chan peer.AddrInfo\n\n\tif cache, ok := ld.caches[cid]; ok {\n\t\tcache.Lock()\n\t\tsize := min(len(cache.recs), limit)\n\n\t\tout = make(chan peer.AddrInfo, size) // make the channel\n\n\t\tfor _, rec := range cache.recs {\n\t\t\tout <- *rec.peer\n\t\t\tsize--\n\t\t\tif size == 0 {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tcache.Unlock()\n\t} else {\n\t\tout = make(chan peer.AddrInfo) // make the channel\n\t}\n\n\tclose(out)\n\treturn out, nil\n}\n\nfunc (ld *LocalDiscovery) Subscribe(ctx context.Context, cid string, opts ...discovery.Option) (<-chan peer.AddrInfo, error) {\n\tld.logger.Debug(\"starting subscribe\", logutil.PrivateString(\"ns\", cid))\n\tcpeer := make(chan peer.AddrInfo)\n\n\t// Get options\n\tvar options discovery.Options\n\terr := options.Apply(opts...)\n\tif err != nil {\n\t\tclose(cpeer)\n\t\treturn cpeer, err\n\t}\n\n\tlimit := options.Limit\n\tif limit == 0 || limit > maxLimit {\n\t\tlimit = maxLimit\n\t}\n\n\tld.muCaches.Lock()\n\tcache, ok := ld.caches[cid]\n\tif !ok {\n\t\tcache = newLinkedCache()\n\t\tld.caches[cid] = cache\n\t}\n\tld.muCaches.Unlock()\n\n\tctx, cancel := context.WithCancel(ctx)\n\tgo func() {\n\t\t<-ctx.Done()\n\t\tcancel()\n\t\tcache.cond.Broadcast()\n\t}()\n\n\tcache.Lock()\n\tcurrent := cache.queue.Front()\n\tcache.Unlock()\n\tcounter := 0\n\n\tgo func() {\n\t\tcache.Lock()\n\t\tfor ctx.Err() == nil && counter <= limit {\n\t\t\tif current == nil {\n\t\t\t\t// unlock the mutex\n\t\t\t\t// wait for new elements in the list\n\t\t\t\tcache.cond.Wait()\n\t\t\t\tcurrent = cache.queue.Back()\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tnext := current.Next()\n\t\t\trec := current.Value.(*recCache)\n\n\t\t\tif time.Now().After(rec.expire) {\n\t\t\t\tld.logger.Debug(\"receiving expired record\",\n\t\t\t\t\tlogutil.PrivateString(\"ns\", cid),\n\t\t\t\t\tzap.Duration(\"since\", time.Since(rec.expire)))\n\n\t\t\t\tdelete(cache.recs, rec.peer.ID)\n\t\t\t\tcache.queue.Remove(current)\n\t\t\t} else {\n\t\t\t\tld.logger.Debug(\"receiving record\", logutil.PrivateString(\"ns\", cid))\n\n\t\t\t\tselect {\n\t\t\t\tcase cpeer <- *rec.peer:\n\t\t\t\t\tcounter++\n\t\t\t\tcase <-ctx.Done():\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// move our pointer forward\n\t\t\tcurrent = next\n\t\t}\n\t\tclose(cpeer)\n\t\tcache.Unlock()\n\n\t\tld.logger.Debug(\"find peers ended\", logutil.PrivateString(\"ns\", cid), zap.Error(ctx.Err()))\n\t}()\n\n\treturn cpeer, nil\n}\n\nfunc (ld *LocalDiscovery) getLocalReccord() *Records {\n\trecords := []*Record{}\n\tnow := time.Now()\n\n\tld.muRecs.Lock()\n\tfor cid, expire := range ld.recs {\n\t\t// if expired remove from cache\n\t\tif now.After(expire) {\n\t\t\tdelete(ld.recs, cid)\n\t\t\tcontinue\n\t\t}\n\n\t\trecords = append(records, &Record{\n\t\t\tCid:    cid,\n\t\t\tExpire: expire.Unix(),\n\t\t})\n\t}\n\tld.muRecs.Unlock()\n\n\treturn &Records{Records: records}\n}\n\nfunc (ld *LocalDiscovery) Unregister(_ context.Context, cid string, _ ...discovery.Option) error {\n\tld.muRecs.Lock()\n\tdelete(ld.recs, cid)\n\tld.muRecs.Unlock()\n\treturn nil\n}\n\nfunc (ld *LocalDiscovery) sendRecordsToProximityPeers(ctx context.Context, records *Records) {\n\tconns := ld.h.Network().Conns()\n\twg := sync.WaitGroup{}\n\n\tfor _, c := range conns {\n\t\tif manet.IsPrivateAddr(c.RemoteMultiaddr()) || isProximityProtocol(c.RemoteMultiaddr()) {\n\t\t\twg.Add(1)\n\t\t\tgo func(c network.Conn) {\n\t\t\t\tld.logger.Debug(\"sending records to peer\", zap.Stringer(\"peer\", c.RemotePeer()))\n\n\t\t\t\tif err := ld.sendRecordsTo(ctx, c.RemotePeer(), records); err != nil {\n\t\t\t\t\tld.logger.Warn(\"unable to send records to newly connected peer\",\n\t\t\t\t\t\tzap.Error(err), zap.Stringer(\"peer\", c.RemotePeer()))\n\t\t\t\t}\n\n\t\t\t\twg.Done()\n\t\t\t}(c)\n\t\t}\n\t}\n\n\twg.Wait()\n}\n\n// Called when is a stream is opened by a remote peer\n// Receive remote peer cache and put it in our local cache\nfunc (ld *LocalDiscovery) handleStream(s network.Stream) {\n\tdefer s.Reset() // nolint:errcheck\n\n\treader := protoio.NewDelimitedReader(s, 2048)\n\trecords := Records{}\n\tif err := reader.ReadMsg(&records); err != nil {\n\t\tld.logger.Error(\"handleStream receive an invalid local record\", zap.Error(err))\n\t\treturn\n\t}\n\n\tinfo := peer.AddrInfo{\n\t\tID:    s.Conn().RemotePeer(),\n\t\tAddrs: []ma.Multiaddr{s.Conn().RemoteMultiaddr()},\n\t}\n\n\t// fill the remote cache\n\tfor _, record := range records.Records {\n\t\texpire := time.Unix(record.Expire, 0)\n\n\t\tld.logger.Debug(\"saving remote record in cache\",\n\t\t\tlogutil.PrivateString(\"remoteID\", s.Conn().RemotePeer().String()),\n\t\t\tlogutil.PrivateString(\"CID\", record.Cid),\n\t\t\tzap.Time(\"expire\", expire))\n\n\t\tld.muCaches.Lock()\n\t\tcache, ok := ld.caches[record.Cid]\n\t\tif !ok {\n\t\t\tcache = newLinkedCache()\n\t\t\tld.caches[record.Cid] = cache\n\t\t}\n\t\tld.muCaches.Unlock()\n\n\t\trec := &recCache{\n\t\t\tpeer:   &info,\n\t\t\ttopic:  record.Cid,\n\t\t\texpire: expire,\n\t\t}\n\n\t\t// update the given cache record and signal to other routine that we got an update\n\t\tcache.Lock()\n\t\tif cacheRec, ok := cache.recs[info.ID]; ok {\n\t\t\tcacheRec.peer = &info\n\t\t\tcacheRec.expire = expire\n\t\t} else {\n\t\t\tcache.recs[info.ID] = rec\n\t\t\tcache.queue.PushBack(rec)\n\t\t}\n\n\t\tcache.cond.Broadcast()\n\t\tcache.Unlock()\n\t}\n}\n\nfunc (ld *LocalDiscovery) sendRecordsTo(ctx context.Context, p peer.ID, records *Records) error {\n\ts, err := ld.h.NewStream(ctx, p, recProtocolID)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to create stream: %w\", err)\n\t}\n\tdefer s.Close()\n\n\tpbw := protoio.NewDelimitedWriter(s)\n\tif err := pbw.WriteMsg(records); err != nil {\n\t\treturn fmt.Errorf(\"write error: %w\", err)\n\t}\n\n\treturn nil\n}\n\nfunc isProximityProtocol(addr ma.Multiaddr) bool {\n\tfor _, p := range addr.Protocols() {\n\t\tswitch p.Code {\n\t\tcase ble.ProtocolCode, mc.ProtocolCode, nearby.ProtocolCode:\n\t\t\treturn true\n\t\tdefault:\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (ld *LocalDiscovery) handleConnection(ctx context.Context, p peer.ID) {\n\tif p == ld.h.ID() {\n\t\treturn\n\t}\n\n\t// check if we use a proximity addrs with this peer\n\tconns := ld.h.Network().ConnsToPeer(p)\n\tfor _, conn := range conns {\n\t\tif manet.IsPrivateAddr(conn.RemoteMultiaddr()) || isProximityProtocol(conn.RemoteMultiaddr()) {\n\t\t\tgo func() {\n\t\t\t\trecords := ld.getLocalReccord()\n\t\t\t\tif err := ld.sendRecordsTo(ctx, p, records); err != nil {\n\t\t\t\t\tld.logger.Warn(\"unable to send local record\",\n\t\t\t\t\t\tlogutil.PrivateString(\"peer\", p.String()),\n\t\t\t\t\t\tzap.Int(\"records\", len(records.Records)),\n\t\t\t\t\t\tzap.Error(err))\n\t\t\t\t} else {\n\t\t\t\t\tld.logger.Info(\"send topics to local peer\", logutil.PrivateString(\"peer\", conn.RemotePeer().String()))\n\t\t\t\t}\n\t\t\t}()\n\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc (ld *LocalDiscovery) monitorConnection(ctx context.Context) error {\n\tsub, err := ld.h.EventBus().Subscribe([]any{\n\t\tnew(event.EvtPeerConnectednessChanged),\n\t\tnew(event.EvtPeerProtocolsUpdated),\n\t}, eventbus.Name(\"weshnet/tinder/monitor-connection\"))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to subscribe to `EvtPeerConnectednessChanged`: %w\", err)\n\t}\n\n\t// check already connected peers\n\tfor _, p := range ld.h.Peerstore().Peers() {\n\t\tif ld.h.Network().Connectedness(p) == network.Connected {\n\t\t\tld.handleConnection(ctx, p)\n\t\t}\n\t}\n\n\tgo func() {\n\t\tdefer sub.Close()\n\t\tfor {\n\t\t\tvar e any\n\t\t\tselect {\n\t\t\tcase e = <-sub.Out():\n\t\t\tcase <-ctx.Done():\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tswitch evt := e.(type) {\n\t\t\tcase event.EvtPeerConnectednessChanged:\n\t\t\t\t// send record to connected peer only\n\t\t\t\tif evt.Connectedness == network.Connected {\n\t\t\t\t\tld.handleConnection(ctx, evt.Peer)\n\t\t\t\t}\n\t\t\tcase event.EvtPeerProtocolsUpdated:\n\t\t\t\tif slices.Contains(evt.Added, recProtocolID) {\n\t\t\t\t\tld.handleConnection(ctx, evt.Peer)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}()\n\n\treturn nil\n}\n\nfunc (ld *LocalDiscovery) Name() string {\n\treturn LocalDiscoveryName\n}\n"
  },
  {
    "path": "pkg/tinder/driver_localdiscovery_test.go",
    "content": "package tinder\n\nimport (\n\t\"context\"\n\t\"math/rand\"\n\t\"testing\"\n\t\"time\"\n\n\tmocknet \"github.com/libp2p/go-libp2p/p2p/net/mock\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"berty.tech/weshnet/v2/pkg/testutil\"\n)\n\nfunc TestServiceLocalDiscorvery(t *testing.T) {\n\tctx := context.Background()\n\tmn := mocknet.New()\n\tdefer mn.Close()\n\n\tlogger, cleanup := testutil.Logger(t)\n\tdefer cleanup()\n\n\tp1 := genLocalPeer(t, mn)\n\tp2 := genLocalPeer(t, mn)\n\n\terr := mn.LinkAll()\n\trequire.NoError(t, err)\n\n\tdisc1, err := NewLocalDiscovery(logger, p1, rand.New(rand.NewSource(rand.Int63())))\n\trequire.NoError(t, err)\n\n\tdisc2, err := NewLocalDiscovery(logger, p2, rand.New(rand.NewSource(rand.Int63())))\n\trequire.NoError(t, err)\n\n\ts1, err := NewService(p1, logger, disc1)\n\trequire.NoError(t, err)\n\n\ts2, err := NewService(p2, logger, disc2)\n\trequire.NoError(t, err)\n\n\tconst topic = \"test_topic\"\n\n\ts1.StartAdvertises(ctx, topic)\n\n\t// try a first lookup, should find nothing\n\t{\n\t\tout := s2.FindPeers(ctx, topic)\n\t\tpeers := testPeersChanToSlice(t, out)\n\t\trequire.Len(t, peers, 0, \"no peer should be available\")\n\t}\n\n\t{\n\t\t// start a subscribe and wait for connection\n\t\tsub := s2.Subscribe(topic)\n\t\tdefer sub.Close()\n\n\t\terr = mn.ConnectAllButSelf()\n\t\trequire.NoError(t, err)\n\n\t\tp, err := testWaitForPeers(t, sub.Out(), time.Second*5)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, p1.ID(), p.ID)\n\t\trequire.Equal(t, p1.Addrs(), p.Addrs)\n\t}\n\n\t// try a lookup again, this time we should have some peers\n\t{\n\t\tout := s2.FindPeers(ctx, topic)\n\t\tp, err := testWaitForPeers(t, out, time.Second*5)\n\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, p1.ID(), p.ID)\n\t\trequire.Equal(t, p1.Addrs(), p.Addrs)\n\t}\n}\n\nfunc TestServiceLocalDiscorveryBeforeProtocolRegister(t *testing.T) {\n\tconst topic = \"test_topic\"\n\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\tmn := mocknet.New()\n\tdefer mn.Close()\n\n\tlogger, cleanup := testutil.Logger(t)\n\tdefer cleanup()\n\n\tp1 := genLocalPeer(t, mn)\n\tp2 := genLocalPeer(t, mn)\n\n\terr := mn.LinkAll()\n\trequire.NoError(t, err)\n\n\tdisc1, err := NewLocalDiscovery(logger, p1, rand.New(rand.NewSource(rand.Int63())))\n\trequire.NoError(t, err)\n\n\t// create service for peer 1\n\ts1, err := NewService(p1, logger, disc1)\n\trequire.NoError(t, err)\n\n\t// start advertising\n\ts1.StartAdvertises(ctx, topic)\n\n\t// connect both peer BEFORE registering local discovery protocol for peer 2\n\terr = mn.ConnectAllButSelf()\n\trequire.NoError(t, err)\n\n\t// let some time to peers to connect and trigger protocol exchange\n\ttime.Sleep(time.Millisecond * 200)\n\n\t// register p2 local discovery\n\tdisc2, err := NewLocalDiscovery(logger, p2, rand.New(rand.NewSource(rand.Int63())))\n\trequire.NoError(t, err)\n\n\ts2, err := NewService(p2, logger, disc2)\n\trequire.NoError(t, err)\n\n\t// start subscribe and wait for connection\n\tsub := s2.Subscribe(topic)\n\tdefer sub.Close()\n\n\t// pull to fetch current peers on the topic\n\tsub.Pull()\n\n\tselect {\n\tcase p := <-sub.Out():\n\t\trequire.Equal(t, p.ID, p1.ID())\n\tcase <-time.After(time.Second * 5):\n\t\trequire.FailNow(t, \"unable to wait for peer on local discovery\")\n\t}\n}\n"
  },
  {
    "path": "pkg/tinder/driver_mock.go",
    "content": "package tinder\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"slices\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/libp2p/go-libp2p/core/discovery\"\n\t\"github.com/libp2p/go-libp2p/core/host\"\n\t\"github.com/libp2p/go-libp2p/core/peer\"\n)\n\ntype MockDriverServer struct {\n\tpc     *peersCache\n\ttopics map[string] /* topic */ map[peer.ID]time.Time\n\tmx     sync.RWMutex\n}\n\nfunc NewMockDriverServer() *MockDriverServer {\n\treturn &MockDriverServer{\n\t\ttopics: make(map[string]map[peer.ID]time.Time),\n\t\tpc:     newPeerCache(),\n\t}\n}\n\nfunc (s *MockDriverServer) Advertise(topic string, info peer.AddrInfo, ttl time.Duration) {\n\ts.mx.Lock()\n\texpires, ok := s.topics[topic]\n\tif !ok {\n\t\texpires = map[peer.ID]time.Time{}\n\t\ts.topics[topic] = expires\n\t}\n\texpires[info.ID] = time.Now().Add(ttl)\n\ts.mx.Unlock()\n\n\ts.pc.UpdatePeer(topic, info)\n}\n\nfunc (s *MockDriverServer) FindPeers(topic string, limit int) <-chan peer.AddrInfo {\n\ts.mx.Lock()\n\tdefer s.mx.Unlock()\n\n\tpeers := s.pc.GetPeersForTopics(topic)\n\tif len(peers) < limit {\n\t\tlimit = len(peers)\n\t}\n\n\texpires, ok := s.topics[topic]\n\tif !ok {\n\t\texpires = map[peer.ID]time.Time{}\n\t\ts.topics[topic] = expires\n\t}\n\n\tout := make(chan peer.AddrInfo, limit)\n\tfor i := 0; i < limit; i++ {\n\t\tpeer := peers[i]\n\t\tif expire, ok := expires[peer.ID]; ok {\n\t\t\tif time.Now().Before(expire) {\n\t\t\t\tout <- peer\n\t\t\t} else {\n\t\t\t\tdelete(expires, peer.ID)\n\t\t\t}\n\t\t}\n\t}\n\n\tclose(out)\n\treturn out\n}\n\nfunc (s *MockDriverServer) Unregister(ctx context.Context, topic string, p peer.ID) {\n\ts.mx.Lock()\n\ts.pc.RemoveFromCache(ctx, topic, p)\n\tif expires, ok := s.topics[topic]; ok {\n\t\tdelete(expires, p)\n\t}\n\ts.mx.Unlock()\n}\n\nfunc (s *MockDriverServer) Exist(topic string, _ peer.ID) (ok bool) {\n\tpeers := s.pc.GetPeersForTopics(topic)\n\treturn len(peers) == 1\n}\n\nfunc (s *MockDriverServer) Subscribe(ctx context.Context, topic string, buffsize int) <-chan peer.AddrInfo {\n\ts.mx.Lock()\n\tdefer s.mx.Unlock()\n\n\tstart := time.Now()\n\n\tpeers := s.pc.GetPeersForTopics(topic)\n\tknownPeers := make(PeersUpdate)\n\tfor _, p := range peers {\n\t\tknownPeers[p.ID] = start\n\t}\n\n\tout := make(chan peer.AddrInfo, buffsize)\n\tgo func() {\n\t\tfor {\n\t\t\t// Wait until `PeersCache` differ from `peerCache` peers status\n\t\t\tupdated, ok := s.pc.WaitForPeerUpdate(ctx, topic, knownPeers)\n\t\t\tif !ok {\n\t\t\t\tbreak // context has expired\n\t\t\t}\n\n\t\t\t// send peers\n\t\t\tfor _, peer := range s.pc.GetPeers(updated...) {\n\t\t\t\tout <- peer\n\t\t\t}\n\t\t}\n\n\t\t// we're done here, close the channel and decrement\n\t\tclose(out)\n\t}()\n\n\treturn out\n}\n\nfunc (s *MockDriverServer) WaitForPeer(topic string, p peer.ID, timeout time.Duration) (err error) {\n\tctx, cancel := context.WithTimeout(context.Background(), timeout)\n\tdefer cancel()\n\n\tknownPeers := make(PeersUpdate)\n\n\tfor {\n\t\t// Wait until `PeersCache` differ from `peerCache` peers status\n\t\tupdated, expired := s.pc.WaitForPeerUpdate(ctx, topic, knownPeers)\n\t\tif !expired {\n\t\t\treturn fmt.Errorf(\"timeout while waiting for: %s\", p.String())\n\t\t}\n\n\t\t// send peers\n\t\tif slices.Contains(updated, p) {\n\t\t\treturn nil\n\t\t}\n\t}\n}\n\nfunc (s *MockDriverServer) Client(h host.Host) IDriver {\n\treturn &MockIDriverClient{h: h, serv: s}\n}\n\nvar _ IDriver = (*MockIDriverClient)(nil)\n\ntype MockIDriverClient struct {\n\th    host.Host\n\tserv *MockDriverServer\n}\n\nfunc (s *MockIDriverClient) Name() string {\n\treturn \"mock\"\n}\n\nfunc (s *MockIDriverClient) FindPeers(_ context.Context, topic string, opts ...discovery.Option) (<-chan peer.AddrInfo, error) {\n\tvar options discovery.Options\n\terr := options.Apply(opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif options.Ttl == 0 {\n\t\toptions.Limit = 1000\n\t}\n\n\treturn s.serv.FindPeers(topic, options.Limit), nil\n}\n\nfunc (s *MockIDriverClient) Advertise(_ context.Context, topic string, opts ...discovery.Option) (time.Duration, error) {\n\tvar options discovery.Options\n\terr := options.Apply(opts...)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\tif options.Ttl == 0 {\n\t\toptions.Ttl = time.Second * 10\n\t}\n\n\tinfo := s.h.Peerstore().PeerInfo(s.h.ID())\n\ts.serv.Advertise(topic, info, options.Ttl)\n\treturn options.Ttl, nil\n}\n\nfunc (s *MockIDriverClient) Subscribe(ctx context.Context, topic string, opts ...discovery.Option) (<-chan peer.AddrInfo, error) {\n\tvar options discovery.Options\n\terr := options.Apply(opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tv := options.Other[optionSubscribeBufferSize]\n\tvar buffsize int\n\tvar ok bool\n\tif buffsize, ok = v.(int); !ok || buffsize == 0 {\n\t\tbuffsize = 16\n\t}\n\n\tout := s.serv.Subscribe(ctx, topic, buffsize)\n\treturn out, nil\n}\n\nfunc (s *MockIDriverClient) Unregister(ctx context.Context, topic string, _ ...discovery.Option) error {\n\ts.serv.Unregister(ctx, topic, s.h.ID())\n\treturn nil\n}\n\ntype discOption string\n\nconst (\n\toptionSubscribeBufferSize discOption = \"tinder_subsize\"\n)\n\nfunc MockBufferSize(size int) discovery.Option {\n\treturn func(opts *discovery.Options) error {\n\t\tif opts.Other == nil {\n\t\t\topts.Other = make(map[any]any)\n\t\t}\n\n\t\topts.Other[optionSubscribeBufferSize] = size\n\t\treturn nil\n\t}\n}\n"
  },
  {
    "path": "pkg/tinder/driver_mock_test.go",
    "content": "package tinder\n\nimport (\n\t\"context\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/libp2p/go-libp2p/core/peer\"\n\tmocknet \"github.com/libp2p/go-libp2p/p2p/net/mock\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestMockAdvertise(t *testing.T) {\n\tconst topic = \"test_topic'\"\n\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\tmn := mocknet.New()\n\tdefer mn.Close()\n\n\tp1, err := mn.GenPeer()\n\trequire.NoError(t, err)\n\n\tserver := NewMockDriverServer()\n\tclient := server.Client(p1)\n\n\tinfo1 := p1.Peerstore().PeerInfo(p1.ID())\n\n\trequire.False(t, server.Exist(topic, info1.ID))\n\n\tttl, err := client.Advertise(ctx, topic)\n\trequire.NoError(t, err)\n\trequire.Greater(t, ttl, time.Duration(0))\n\trequire.True(t, server.Exist(topic, info1.ID))\n}\n\nfunc TestMockFindPeers(t *testing.T) {\n\tconst topic = \"test_topic'\"\n\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\tmn := mocknet.New()\n\tdefer mn.Close()\n\n\tp1, err := mn.GenPeer()\n\trequire.NoError(t, err)\n\n\tp2, err := mn.GenPeer()\n\trequire.NoError(t, err)\n\n\tserver := NewMockDriverServer()\n\tclient1 := server.Client(p1)\n\tclient2 := server.Client(p2)\n\n\tinfo1 := p1.Peerstore().PeerInfo(p1.ID())\n\tserver.Advertise(topic, info1, time.Minute)\n\n\tfor _, client := range []IDriver{client1, client2} {\n\t\tout, err := client.FindPeers(ctx, topic)\n\t\trequire.NoError(t, err)\n\n\t\tvar peers []peer.AddrInfo\n\t\tfor p := range out {\n\t\t\tpeers = append(peers, p)\n\t\t}\n\n\t\trequire.Len(t, peers, 1)\n\t\tassert.Equal(t, info1, peers[0])\n\t}\n\n\tinfo2 := p2.Peerstore().PeerInfo(p2.ID())\n\tserver.Advertise(topic, info2, time.Minute)\n\n\tfor _, client := range []IDriver{client1, client2} {\n\t\tout, err := client.FindPeers(ctx, topic)\n\t\trequire.NoError(t, err)\n\n\t\tvar peers []peer.AddrInfo\n\t\tfor p := range out {\n\t\t\tpeers = append(peers, p)\n\t\t}\n\t\trequire.Len(t, peers, 2)\n\t}\n}\n\nfunc TestMockSubscribe(t *testing.T) {\n\tconst topic = \"test_topic'\"\n\n\tmn := mocknet.New()\n\tdefer mn.Close()\n\n\tp1, err := mn.GenPeer()\n\trequire.NoError(t, err)\n\n\tp2, err := mn.GenPeer()\n\trequire.NoError(t, err)\n\tinfo2 := p1.Peerstore().PeerInfo(p2.ID())\n\n\tp3, err := mn.GenPeer()\n\trequire.NoError(t, err)\n\tinfo3 := p1.Peerstore().PeerInfo(p3.ID())\n\n\tserver := NewMockDriverServer()\n\tclient1 := server.Client(p1)\n\n\t{\n\t\tctx, cancel := context.WithCancel(context.Background())\n\t\tdefer cancel()\n\n\t\tout, err := client1.Subscribe(ctx, topic, MockBufferSize(2))\n\t\trequire.NoError(t, err)\n\n\t\tserver.Advertise(topic, info2, time.Minute)\n\t\tserver.Advertise(topic, info3, time.Minute)\n\n\t\tpeers := map[peer.ID]peer.AddrInfo{}\n\t\tfor i := 0; i < 2; i++ {\n\t\t\tpeer := <-out\n\t\t\tpeers[peer.ID] = peer\n\t\t}\n\n\t\tp2, ok := peers[info2.ID]\n\t\trequire.True(t, ok)\n\t\trequire.Equal(t, info2, p2)\n\n\t\tp3, ok := peers[info3.ID]\n\t\trequire.True(t, ok)\n\t\trequire.Equal(t, info3, p3)\n\n\t\tcancel()\n\t\t// should be close if context expire\n\t\trequire.Eventually(t, func() bool {\n\t\t\tselect {\n\t\t\tcase <-out:\n\t\t\t\treturn true\n\t\t\tdefault:\n\t\t\t\treturn false\n\t\t\t}\n\t\t}, time.Second, time.Millisecond*10)\n\n\t}\n}\n\nfunc TestMockUnregister(t *testing.T) {\n\tconst topic = \"test_topic'\"\n\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\tmn := mocknet.New()\n\tdefer mn.Close()\n\n\tp1, err := mn.GenPeer()\n\trequire.NoError(t, err)\n\n\tserver := NewMockDriverServer()\n\tclient := server.Client(p1)\n\n\tinfo1 := p1.Peerstore().PeerInfo(p1.ID())\n\n\tserver.Advertise(topic, info1, time.Minute)\n\trequire.True(t, server.Exist(topic, info1.ID))\n\n\terr = client.Unregister(ctx, topic)\n\trequire.NoError(t, err)\n\trequire.False(t, server.Exist(topic, info1.ID))\n}\n"
  },
  {
    "path": "pkg/tinder/driver_rdvp.go",
    "content": "// from https://github.com/berty/go-libp2p-rendezvous/blob/implement-spec/discovery.go\n\npackage tinder\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"math\"\n\tmrand \"math/rand\"\n\t\"sync\"\n\t\"time\"\n\n\tp2p_rp \"github.com/berty/go-libp2p-rendezvous\"\n\t\"github.com/libp2p/go-libp2p/core/discovery\"\n\t\"github.com/libp2p/go-libp2p/core/host\"\n\t\"github.com/libp2p/go-libp2p/core/peer\"\n\t\"go.uber.org/zap\"\n)\n\ntype rendezvousDiscovery struct {\n\tlogger       *zap.Logger\n\trp           p2p_rp.RendezvousPoint\n\tsynccls      []p2p_rp.RendezvousSyncClient\n\tpeerCache    map[string]*rpCache\n\tpeerCacheMux sync.RWMutex\n\trng          *mrand.Rand\n\trngMux       sync.Mutex\n\tselfID       peer.ID\n}\n\ntype rpCache struct {\n\trecs   map[peer.ID]*rpRecord\n\tcookie []byte\n\tmux    sync.Mutex\n}\n\ntype rpRecord struct {\n\tpeer   peer.AddrInfo\n\texpire int64\n}\n\nfunc NewRendezvousDiscovery(logger *zap.Logger, host host.Host, rdvPeer peer.ID, factory p2p_rp.AddrsFactory, rng *mrand.Rand, emitters ...p2p_rp.RendezvousSyncClient) IDriver {\n\trp := p2p_rp.NewRendezvousPoint(host, rdvPeer, p2p_rp.ClientWithAddrsFactory(factory))\n\treturn &rendezvousDiscovery{\n\t\tlogger:    logger.Named(\"tinder/rdvp\"),\n\t\trp:        rp,\n\t\trng:       rng,\n\t\tsynccls:   emitters,\n\t\tpeerCache: make(map[string]*rpCache),\n\t\tselfID:    host.ID(),\n\t}\n}\n\nfunc (c *rendezvousDiscovery) Advertise(ctx context.Context, topic string, opts ...discovery.Option) (time.Duration, error) {\n\t// Get options\n\tvar options discovery.Options\n\terr := options.Apply(opts...)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\tttl := options.Ttl\n\tvar ttlSeconds int\n\n\tif ttl == 0 {\n\t\tttlSeconds = 7200\n\t} else {\n\t\tttlSeconds = int(math.Round(ttl.Seconds()))\n\t}\n\n\trttl, err := c.rp.Register(ctx, topic, ttlSeconds)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn rttl, nil\n}\n\nfunc (c *rendezvousDiscovery) FindPeers(ctx context.Context, topic string, opts ...discovery.Option) (<-chan peer.AddrInfo, error) {\n\t// Get options\n\tvar options discovery.Options\n\terr := options.Apply(opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tconst maxLimit = 1000\n\tlimit := options.Limit\n\tif limit == 0 || limit > maxLimit {\n\t\tlimit = maxLimit\n\t}\n\n\treturn c.findPeers(ctx, topic, limit)\n}\n\nfunc (c *rendezvousDiscovery) findPeers(ctx context.Context, topic string, limit int) (<-chan peer.AddrInfo, error) {\n\t// Get cached peers\n\tvar err error\n\n\tc.peerCacheMux.RLock()\n\tcache, ok := c.peerCache[topic]\n\tc.peerCacheMux.RUnlock()\n\tif !ok {\n\t\tc.peerCacheMux.Lock()\n\t\tcache, ok = c.peerCache[topic]\n\t\tif !ok {\n\t\t\tcache = &rpCache{recs: make(map[peer.ID]*rpRecord)}\n\t\t\tc.peerCache[topic] = cache\n\t\t}\n\t\tc.peerCacheMux.Unlock()\n\t}\n\n\tcache.mux.Lock()\n\tdefer cache.mux.Unlock()\n\n\t// Remove all expired entries from cache\n\tcurrentTime := time.Now().Unix()\n\tnewCacheSize := len(cache.recs)\n\n\tfor p := range cache.recs {\n\t\trec := cache.recs[p]\n\t\tif rec.expire < currentTime {\n\t\t\tnewCacheSize--\n\t\t\tdelete(cache.recs, p)\n\t\t}\n\t}\n\n\tcookie := cache.cookie\n\n\t// Discover new records if we don't have enough\n\tif newCacheSize < limit {\n\t\t// TODO: Should we return error even if we have valid cached results?\n\t\tvar regs []p2p_rp.Registration\n\t\tvar newCookie []byte\n\n\t\tif regs, newCookie, err = c.rp.Discover(ctx, topic, limit, cookie); err == nil {\n\t\t\tfor _, reg := range regs {\n\t\t\t\trec := &rpRecord{peer: reg.Peer, expire: int64(reg.Ttl) + currentTime}\n\t\t\t\tcache.recs[rec.peer.ID] = rec\n\t\t\t}\n\t\t\tcache.cookie = newCookie\n\t\t} else {\n\t\t\terr = fmt.Errorf(\"unable to run discover: %w\", err)\n\t\t}\n\t}\n\n\t// Randomize and fill channel with available records\n\tcount := min(limit, len(cache.recs))\n\n\tchPeer := make(chan peer.AddrInfo, count)\n\n\tc.rngMux.Lock()\n\tperm := c.rng.Perm(len(cache.recs))[0:count]\n\tc.rngMux.Unlock()\n\n\tpermSet := make(map[int]int)\n\tfor i, v := range perm {\n\t\tpermSet[v] = i\n\t}\n\n\tsendLst := make([]*peer.AddrInfo, count)\n\titer := 0\n\tfor k := range cache.recs {\n\t\tif sendIndex, ok := permSet[iter]; ok {\n\t\t\tsendLst[sendIndex] = &cache.recs[k].peer\n\t\t}\n\t\titer++\n\t}\n\n\tfor _, send := range sendLst {\n\t\tchPeer <- *send\n\t}\n\n\tclose(chPeer)\n\treturn chPeer, err\n}\n\nfunc (c *rendezvousDiscovery) Subscribe(ctx context.Context, topic string, _ ...discovery.Option) (<-chan peer.AddrInfo, error) {\n\tch, err := c.getSubscribtionForTopic(ctx, topic)\n\treturn ch, err\n}\n\nfunc (c *rendezvousDiscovery) Unregister(ctx context.Context, topic string, _ ...discovery.Option) error {\n\tc.peerCacheMux.RLock()\n\tcache, ok := c.peerCache[topic]\n\tif ok {\n\t\tcache.mux.Lock()\n\t\tdelete(cache.recs, c.selfID)\n\t\tcache.mux.Unlock()\n\t}\n\tc.peerCacheMux.RUnlock()\n\treturn c.rp.Unregister(ctx, topic)\n}\n\nfunc (c *rendezvousDiscovery) Name() string {\n\treturn \"rdvp\"\n}\n\nfunc (c *rendezvousDiscovery) getSubscribtionForTopic(ctx context.Context, topic string) (<-chan peer.AddrInfo, error) {\n\tif len(c.synccls) > 0 {\n\t\treturn c.rp.DiscoverSubscribe(ctx, topic, c.synccls)\n\t}\n\n\tch := make(chan peer.AddrInfo, 16)\n\tcreg, err := c.rp.DiscoverAsync(ctx, topic)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to start discovery async: %w\", err)\n\t}\n\n\tgo func() {\n\t\tdefer close(ch)\n\t\tfor reg := range creg {\n\t\t\tch <- reg.Peer\n\t\t}\n\t}()\n\n\treturn ch, nil\n}\n\n// nolint:gochecknoinits\nfunc init() {\n\tp2p_rp.DiscoverAsyncInterval = time.Second * 30\n}\n"
  },
  {
    "path": "pkg/tinder/driver_service_test.go",
    "content": "package tinder\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"math/rand\"\n\t\"testing\"\n\t\"time\"\n\n\trendezvous \"github.com/berty/go-libp2p-rendezvous\"\n\tdht \"github.com/libp2p/go-libp2p-kad-dht\"\n\t\"github.com/libp2p/go-libp2p/core/host\"\n\t\"github.com/libp2p/go-libp2p/core/peer\"\n\tmocknet \"github.com/libp2p/go-libp2p/p2p/net/mock\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/zap\"\n\n\t\"berty.tech/weshnet/v2/pkg/testutil\"\n)\n\ntype testMakeDriver = func(t *testing.T, logger *zap.Logger, p host.Host) IDriver\n\nfunc TestMultipleDriversSubscribe(t *testing.T) {\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\tmn := mocknet.New()\n\tdefer mn.Close()\n\n\ttargetRdvp, _ := makeRendezvousService(t, mn)\n\n\tcases := []struct {\n\t\tName  string\n\t\tMakes []testMakeDriver\n\t}{\n\t\t{\"mocked driver\", []testMakeDriver{\n\t\t\ttestMakeMockedDriverFactory(NewMockDriverServer()),\n\t\t}},\n\t\t{\"localdisc driver\", []testMakeDriver{\n\t\t\ttestMakeLocalDiscoveryDriver,\n\t\t}},\n\t\t{\"rendezvous driver\", []testMakeDriver{\n\t\t\ttestMakeRendezVousFactory(targetRdvp),\n\t\t}},\n\t\t{\"mocked+rendezvous driver\", []testMakeDriver{\n\t\t\ttestMakeMockedDriverFactory(NewMockDriverServer()),\n\t\t\ttestMakeRendezVousFactory(targetRdvp),\n\t\t}},\n\t\t{\"localdisc+mocked+rendezvous driver\", []testMakeDriver{\n\t\t\ttestMakeLocalDiscoveryDriver,\n\t\t\ttestMakeMockedDriverFactory(NewMockDriverServer()),\n\t\t\ttestMakeRendezVousFactory(targetRdvp),\n\t\t}},\n\t}\n\n\tfor i, tc := range cases {\n\t\ttopic := fmt.Sprintf(\"test_topic_%d\", i)\n\t\tt.Run(tc.Name, func(t *testing.T) {\n\t\t\ttestMultipleDriversSubscribe(t, ctx, mn, topic, tc.Makes...)\n\t\t})\n\t}\n}\n\nfunc testMultipleDriversSubscribe(t *testing.T, ctx context.Context, mn mocknet.Mocknet, topic string, makers ...testMakeDriver) {\n\tctx, cancel := context.WithCancel(ctx)\n\tdefer cancel()\n\n\tlogger, cleanup := testutil.Logger(t)\n\tdefer cleanup()\n\n\tvar err error\n\tvar p1, p2 host.Host\n\tvar s1, s2 *Service\n\n\t{\n\t\tp1 = genLocalPeer(t, mn)\n\t\tdrivers := []IDriver{}\n\t\tfor _, maker := range makers {\n\t\t\tdrivers = append(drivers, maker(t, logger, p1))\n\t\t}\n\t\ts1, err = NewService(p1, logger, drivers...)\n\t\trequire.NoError(t, err)\n\t}\n\n\t{\n\t\tp2 = genLocalPeer(t, mn)\n\t\tdrivers := []IDriver{}\n\t\tfor _, maker := range makers {\n\t\t\tdrivers = append(drivers, maker(t, logger, p2))\n\t\t}\n\t\ts2, err = NewService(p2, logger, drivers...)\n\t\trequire.NoError(t, err)\n\t}\n\n\terr = mn.LinkAll()\n\trequire.NoError(t, err)\n\terr = mn.ConnectAllButSelf()\n\trequire.NoError(t, err)\n\n\t// try a first lookup, should find nothing\n\t{\n\t\tout := s2.FindPeers(ctx, topic)\n\t\tpeers := testPeersChanToSlice(t, out)\n\t\trequire.Len(t, peers, 0, \"no peer should be available\")\n\t}\n\n\t// subscribe...\n\tsub := s2.Subscribe(topic)\n\trequire.NoError(t, err)\n\n\t// ...then advertise\n\terr = s1.StartAdvertises(ctx, topic)\n\trequire.NoError(t, err)\n\n\t{\n\t\t// should find exactly one peer\n\t\tp, err := testWaitForPeers(t, sub.Out(), time.Second*5)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, p1.ID(), p.ID)\n\t\trequire.Equal(t, p1.Addrs(), p.Addrs)\n\t}\n\n\t{\n\t\t// should not have any peers left in the queue\n\t\tp, err := testWaitForPeers(t, sub.Out(), time.Millisecond*100)\n\t\trequire.Nil(t, p)\n\t\trequire.Error(t, err)\n\t}\n\n\t// try a lookup again, this time we should have some peers\n\t{\n\t\tout := s2.FindPeers(ctx, topic)\n\t\tp, err := testWaitForPeers(t, out, time.Second*5)\n\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, p1.ID(), p.ID)\n\t\trequire.Equal(t, p1.Addrs(), p.Addrs)\n\n\t\t// empty the channel, should not any peers left\n\t\terr = testDrainChannel(t, out, time.Second*5)\n\t\trequire.NoError(t, err)\n\t}\n}\n\nfunc testMakeRendezVousFactory(target peer.ID) testMakeDriver {\n\trng := rand.New(rand.NewSource(rand.Int63()))\n\treturn func(t *testing.T, logger *zap.Logger, p host.Host) IDriver {\n\t\tt.Helper()\n\n\t\tsyncClient := rendezvous.NewSyncInMemClient(context.Background(), p)\n\t\treturn NewRendezvousDiscovery(logger, p, target, PrivateAddrsOnlyFactory, rng, syncClient)\n\t}\n}\n\nfunc testMakeMockedDriverFactory(srv *MockDriverServer) testMakeDriver {\n\treturn func(t *testing.T, logger *zap.Logger, p host.Host) IDriver {\n\t\treturn srv.Client(p)\n\t}\n}\n\nfunc testMakeDHTDriver(t *testing.T, logger *zap.Logger, p host.Host) IDriver {\n\tt.Helper()\n\n\tctx, cancel := context.WithCancel(context.Background())\n\tt.Cleanup(cancel)\n\n\td, err := dht.New(ctx, p)\n\trequire.NoError(t, err)\n\tt.Cleanup(func() { d.Close() })\n\n\treturn NewRoutingDiscoveryDriver(\"dht\", d)\n}\n\nfunc testMakeLocalDiscoveryDriver(t *testing.T, logger *zap.Logger, p host.Host) IDriver {\n\tt.Helper()\n\n\tdriver, err := NewLocalDiscovery(logger, p, rand.New(rand.NewSource(rand.Int63())))\n\trequire.NoError(t, err, \"unable to make local discovery driver\")\n\treturn driver\n}\n"
  },
  {
    "path": "pkg/tinder/filter.go",
    "content": "package tinder\n\nimport (\n\tma \"github.com/multiformats/go-multiaddr\"\n\tmanet \"github.com/multiformats/go-multiaddr/net\"\n)\n\n// keep public addr only\nfunc PublicAddrsOnlyFactory(ms []ma.Multiaddr) []ma.Multiaddr {\n\treturn ma.FilterAddrs(ms, manet.IsPublicAddr)\n}\n\n// keep private addr only\nfunc PrivateAddrsOnlyFactory(ms []ma.Multiaddr) []ma.Multiaddr {\n\treturn ma.FilterAddrs(ms, manet.IsPrivateAddr)\n}\n\nfunc AllAddrsFactory(ms []ma.Multiaddr) []ma.Multiaddr { return ms }\n"
  },
  {
    "path": "pkg/tinder/notify_network.go",
    "content": "package tinder\n\nimport (\n\t\"context\"\n\t\"sync\"\n\n\t\"github.com/libp2p/go-libp2p/core/event\"\n\t\"github.com/libp2p/go-libp2p/core/host\"\n\tbhost \"github.com/libp2p/go-libp2p/p2p/host/basic\"\n\tma \"github.com/multiformats/go-multiaddr\"\n\t\"go.uber.org/zap\"\n\n\t\"berty.tech/weshnet/v2/internal/notify\"\n\t\"berty.tech/weshnet/v2/pkg/logutil\"\n)\n\ntype AddrsFilter = bhost.AddrsFactory\n\ntype NetworkUpdate struct {\n\tlogger       *zap.Logger\n\tnotify       *notify.Notify\n\tlocker       *sync.Mutex\n\tsub          event.Subscription\n\tonce         sync.Once\n\tcurrentAddrs []ma.Multiaddr\n}\n\nfunc NewNetworkUpdate(logger *zap.Logger, h host.Host) (*NetworkUpdate, error) {\n\tsub, err := h.EventBus().Subscribe(new(event.EvtLocalAddressesUpdated))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tlocker := &sync.Mutex{}\n\tnu := &NetworkUpdate{\n\t\tlogger:       logger,\n\t\tsub:          sub,\n\t\tlocker:       locker,\n\t\tnotify:       notify.New(locker),\n\t\tcurrentAddrs: h.Network().ListenAddresses(),\n\t}\n\n\tgo nu.subscribeToNetworkUpdate()\n\n\tnu.logger.Debug(\"network update subscribe started\")\n\treturn nu, nil\n}\n\nfunc (n *NetworkUpdate) WaitForUpdate(ctx context.Context, currentAddrs []ma.Multiaddr) (diff []ma.Multiaddr, ok bool) {\n\tn.locker.Lock()\n\tdefer n.locker.Unlock()\n\n\tfor {\n\t\t// check for new/removed addrs\n\t\tif diff := diffAddrs(currentAddrs, n.currentAddrs); len(diff) > 0 {\n\t\t\treturn diff, true\n\t\t}\n\n\t\t// wait until context is done or network is updated\n\t\tif ok := n.notify.Wait(ctx); !ok {\n\t\t\treturn []ma.Multiaddr{}, false\n\t\t}\n\t}\n}\n\nfunc (n *NetworkUpdate) GetLastUpdatedAddrs(context.Context) (addrs []ma.Multiaddr) {\n\tn.locker.Lock()\n\taddrs = n.currentAddrs\n\tn.locker.Unlock()\n\treturn\n}\n\nfunc (n *NetworkUpdate) subscribeToNetworkUpdate() {\n\tfor evt := range n.sub.Out() {\n\t\te := evt.(event.EvtLocalAddressesUpdated)\n\t\tif e.Diffs {\n\t\t\t// log diffs\n\t\t\tvar nadd, ndel int\n\t\t\tfor _, uaddr := range e.Current {\n\t\t\t\tswitch uaddr.Action {\n\t\t\t\tcase event.Added:\n\t\t\t\t\tn.logger.Debug(\"new addr\", logutil.PrivateString(\"addr\", uaddr.Address.String()))\n\t\t\t\t\tnadd++\n\t\t\t\tcase event.Removed:\n\t\t\t\t\tn.logger.Debug(\"removed addr\", logutil.PrivateString(\"addr\", uaddr.Address.String()))\n\t\t\t\t\tndel++\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tn.logger.Debug(\"network update\", zap.Int(\"del\", ndel), zap.Int(\"add\", nadd), zap.Int(\"total\", nadd+ndel))\n\n\t\t\t// update current addrs\n\t\t\tn.locker.Lock()\n\t\t\tn.currentAddrs = getAddrsFromUpdatedAddress(e.Current)\n\t\t\tn.notify.Broadcast()\n\t\t\tn.locker.Unlock()\n\t\t}\n\t}\n}\n\nfunc (n *NetworkUpdate) Close() (err error) {\n\t// use once to avoid panic if called twice\n\tn.once.Do(func() {\n\t\terr = n.sub.Close()\n\t})\n\n\treturn err\n}\n\nfunc diffAddrs(a, b []ma.Multiaddr) []ma.Multiaddr {\n\tdiff := []ma.Multiaddr{}\n\n\tseta := make(map[string]ma.Multiaddr, len(a))\n\tfor _, addr := range a {\n\t\tseta[addr.String()] = addr\n\t}\n\n\tsetb := make(map[string]struct{})\n\tfor _, maddr := range b {\n\t\tkey := maddr.String()\n\t\tif _, found := seta[key]; !found {\n\t\t\tdelete(seta, key)\n\t\t\tdiff = append(diff, maddr)\n\t\t} else {\n\t\t\tsetb[key] = struct{}{}\n\t\t}\n\t}\n\n\tfor key, maddr := range seta {\n\t\tif _, found := setb[key]; !found {\n\t\t\tdiff = append(diff, maddr)\n\t\t}\n\t}\n\n\treturn diff\n}\n\nfunc getAddrsFromUpdatedAddress(updated []event.UpdatedAddress) []ma.Multiaddr {\n\taddrs := make([]ma.Multiaddr, len(updated))\n\tfor i, uaddr := range updated {\n\t\taddrs[i] = uaddr.Address\n\t}\n\treturn addrs\n}\n"
  },
  {
    "path": "pkg/tinder/options.go",
    "content": "package tinder\n\ntype Filter map[string]struct{}\n\nfunc (f Filter) ShouldFilter(name string) (yes bool) {\n\tif f == nil {\n\t\treturn\n\t}\n\n\t_, yes = f[name]\n\treturn\n}\n\ntype Options struct {\n\tDriverFilters Filter\n}\n\ntype Option func(opts *Options) error\n\nfunc (o *Options) apply(opts ...Option) error {\n\tfor _, opt := range opts {\n\t\tif err := opt(o); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc FilterOutDrivers(drivers ...string) Option {\n\treturn func(opts *Options) error {\n\t\topts.DriverFilters = map[string]struct{}{}\n\t\tfor _, driver := range drivers {\n\t\t\topts.DriverFilters[driver] = struct{}{}\n\t\t}\n\n\t\treturn nil\n\t}\n}\n"
  },
  {
    "path": "pkg/tinder/peer_cache.go",
    "content": "package tinder\n\nimport (\n\t\"context\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/libp2p/go-libp2p/core/peer\"\n\tma \"github.com/multiformats/go-multiaddr\"\n\n\t\"berty.tech/weshnet/v2/internal/notify\"\n)\n\ntype PeersUpdate map[peer.ID]time.Time\n\nfunc (current PeersUpdate) HasUpdate(tu *topicUpdate) []peer.ID {\n\tpeers := []peer.ID{}\n\tfor p, update := range tu.peerUpdate {\n\t\tif lastUpdate, ok := current[p]; !ok || update.After(lastUpdate) {\n\t\t\tcurrent[p] = update\n\t\t\tpeers = append(peers, p)\n\t\t}\n\t}\n\treturn peers\n}\n\ntype topicUpdate struct {\n\tpeerUpdate map[peer.ID]time.Time\n\tnotify     *notify.Notify\n}\n\ntype peersCache struct {\n\ttopics  map[string]*topicUpdate\n\tmuCache sync.RWMutex\n\n\tpeers   map[peer.ID]peer.AddrInfo\n\tmuPeers sync.RWMutex\n}\n\nfunc newPeerCache() *peersCache {\n\treturn &peersCache{\n\t\ttopics: make(map[string]*topicUpdate),\n\t\tpeers:  make(map[peer.ID]peer.AddrInfo),\n\t}\n}\n\nfunc (c *peersCache) UpdatePeer(topic string, p peer.AddrInfo) (isNew bool) {\n\tc.muPeers.Lock()\n\tdefer c.muPeers.Unlock()\n\n\ttu := c.getTopicUpdate(topic)\n\n\t// update peer update time\n\ttu.notify.L.Lock()\n\tdefer tu.notify.L.Unlock()\n\n\t_, exist := tu.peerUpdate[p.ID]\n\n\t// do we already know this peer ?\n\tif prev, ok := c.peers[p.ID]; ok {\n\t\tif combined := mergeAddrInfos(prev, p); combined != nil {\n\t\t\tc.peers[p.ID] = *combined\n\t\t} else if exist {\n\t\t\t// we already know this peer, and no change have been provided\n\t\t\treturn isNew\n\t\t}\n\t} else {\n\t\tc.peers[p.ID] = p\n\t}\n\n\tt := time.Now()\n\ttu.peerUpdate[p.ID] = t\n\n\t// notify topic that peers has been updated\n\ttu.notify.Broadcast()\n\n\treturn true\n}\n\nfunc (c *peersCache) GetPeers(ids ...peer.ID) (peers []peer.AddrInfo) {\n\tc.muPeers.RLock()\n\tpeers = make([]peer.AddrInfo, len(ids))\n\tfor i, id := range ids {\n\t\tpeers[i] = c.peers[id]\n\t}\n\tc.muPeers.RUnlock()\n\n\treturn\n}\n\nfunc (c *peersCache) GetPeersForTopics(topic string) (peers []peer.AddrInfo) {\n\tc.muPeers.RLock()\n\tdefer c.muPeers.RUnlock()\n\n\ttu := c.getTopicUpdate(topic)\n\n\ttu.notify.L.Lock()\n\tdefer tu.notify.L.Unlock()\n\n\ti := 0\n\tpeers = make([]peer.AddrInfo, len(tu.peerUpdate))\n\tfor peer := range tu.peerUpdate {\n\t\tpeers[i] = c.peers[peer]\n\t\ti++\n\t}\n\n\treturn\n}\n\nfunc (c *peersCache) WaitForPeerUpdate(ctx context.Context, topic string, current PeersUpdate) (updated []peer.ID, ok bool) {\n\ttu := c.getTopicUpdate(topic)\n\n\ttu.notify.L.Lock()\n\tfor ok = true; ok; {\n\t\t// check if there are some diff between local state and the current state\n\t\tif updated = current.HasUpdate(tu); !ok || len(updated) > 0 {\n\t\t\tbreak // we got some update, leave the loop\n\t\t}\n\n\t\t// wait until there is an update on this group or context expire\n\t\t// unlock notify locker\n\t\tok = tu.notify.Wait(ctx)\n\t}\n\ttu.notify.L.Unlock()\n\n\treturn\n}\n\nfunc (c *peersCache) RemoveFromCache(_ context.Context, topic string, p peer.ID) (ok bool) {\n\tc.muCache.Lock()\n\tvar tu *topicUpdate\n\tif tu, ok = c.topics[topic]; ok {\n\t\tif _, ok = tu.peerUpdate[p]; ok {\n\t\t\tdelete(tu.peerUpdate, p)\n\t\t}\n\t}\n\tc.muCache.Unlock()\n\treturn\n}\n\nfunc (c *peersCache) getTopicUpdate(topic string) *topicUpdate {\n\tc.muCache.Lock()\n\ttu, ok := c.topics[topic]\n\tif !ok {\n\t\ttu = &topicUpdate{\n\t\t\tpeerUpdate: make(map[peer.ID]time.Time),\n\t\t\tnotify:     notify.New(&sync.Mutex{}),\n\t\t}\n\t\tc.topics[topic] = tu\n\t}\n\tc.muCache.Unlock()\n\n\treturn tu\n}\n\nfunc mergeAddrInfos(prevAi, newAi peer.AddrInfo) *peer.AddrInfo {\n\tcombinedAddrs := uniqueAddrs(prevAi.Addrs, newAi.Addrs)\n\tif len(combinedAddrs) > len(prevAi.Addrs) {\n\t\tcombinedAi := &peer.AddrInfo{ID: prevAi.ID, Addrs: combinedAddrs}\n\t\treturn combinedAi\n\t}\n\treturn nil\n}\n\n// uniqueAddrs merges input address lists, leave only unique addresses\nfunc uniqueAddrs(addrss ...[]ma.Multiaddr) (uniqueAddrs []ma.Multiaddr) {\n\texists := make(map[string]bool)\n\tfor _, addrs := range addrss {\n\t\tfor _, addr := range addrs {\n\t\t\tk := string(addr.Bytes())\n\t\t\tif exists[k] {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\texists[k] = true\n\t\t\tuniqueAddrs = append(uniqueAddrs, addr)\n\t\t}\n\t}\n\treturn uniqueAddrs\n}\n"
  },
  {
    "path": "pkg/tinder/peer_cache_test.go",
    "content": "package tinder\n\nimport (\n\t\"context\"\n\t\"runtime\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/libp2p/go-libp2p/core/peer\"\n\tma \"github.com/multiformats/go-multiaddr\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestPeerCacheUpdatePeer(t *testing.T) {\n\tconst topic = \"test_topic\"\n\n\tif runtime.GOOS == \"windows\" {\n\t\tt.Skip(\"unittest not consistent on windows, skipping.\")\n\t}\n\n\tpeer := peer.AddrInfo{\n\t\tID: \"hello\",\n\t\tAddrs: []ma.Multiaddr{\n\t\t\tma.StringCast(\"/ip6/::/tcp/1234\"),\n\t\t},\n\t}\n\n\tpc := newPeerCache()\n\tt.Run(\"add new peer\", func(t *testing.T) {\n\t\tpc.UpdatePeer(topic, peer)\n\n\t\ttu, ok := pc.topics[topic]\n\t\trequire.True(t, ok)\n\t\trequire.NotNil(t, tu)\n\n\t\tti, ok := tu.peerUpdate[peer.ID]\n\t\trequire.True(t, ok)\n\t\tassert.True(t, time.Now().After(ti))\n\n\t\tspeer, ok := pc.peers[peer.ID]\n\t\trequire.True(t, ok)\n\t\tassert.Equal(t, peer, speer)\n\t})\n\n\tt.Run(\"edit peer multiaddrs\", func(t *testing.T) {\n\t\tpeer.Addrs = []ma.Multiaddr{ma.StringCast(\"/ip4/1.2.3.4/tcp/1234\")}\n\t\tpc.UpdatePeer(topic, peer)\n\n\t\tspeer, ok := pc.peers[peer.ID]\n\t\trequire.True(t, ok)\n\t\tassert.Len(t, speer.Addrs, 2)\n\t})\n}\n\nfunc TestPeerCacheWaitForUpdate(t *testing.T) {\n\tconst topic = \"test_topic\"\n\n\tpc := newPeerCache()\n\tpu := make(PeersUpdate)\n\n\tctx, cancel := context.WithTimeout(context.Background(), time.Second*5)\n\tout := make(chan []peer.ID)\n\tgo func() {\n\t\tdefer cancel()\n\n\t\tfor {\n\t\t\tupdated, ok := pc.WaitForPeerUpdate(ctx, topic, pu)\n\t\t\tif !ok {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tout <- updated\n\t\t}\n\t}()\n\n\tt.Run(\"should not update on start\", func(t *testing.T) {\n\t\tselect {\n\t\tcase <-out:\n\t\t\trequire.FailNow(t, \"should not have received an update\")\n\t\tcase <-ctx.Done():\n\t\t\trequire.FailNow(t, \"context should not be expired\")\n\t\tcase <-time.After(time.Millisecond * 100):\n\t\t}\n\t})\n\n\tp1 := peer.AddrInfo{\n\t\tID:    \"peer1\",\n\t\tAddrs: []ma.Multiaddr{ma.StringCast(\"/ip4/1.2.3.4/tcp/1234\")},\n\t}\n\n\tt.Run(\"should correctly update\", func(t *testing.T) {\n\t\tpc.UpdatePeer(topic, p1)\n\n\t\tvar updated []peer.ID\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\trequire.NoError(t, ctx.Err())\n\t\tcase updated = <-out:\n\t\t\trequire.Len(t, updated, 1)\n\t\t}\n\n\t\tps := pc.GetPeers(updated...)\n\t\trequire.Len(t, ps, 1)\n\t\tassert.Equal(t, p1, ps[0])\n\t})\n\n\tt.Run(\"should not update on same peer\", func(t *testing.T) {\n\t\tpc.UpdatePeer(topic, p1)\n\n\t\tselect {\n\t\tcase <-out:\n\t\t\trequire.FailNow(t, \"should not have received an update\")\n\t\tcase <-ctx.Done():\n\t\t\trequire.NoError(t, ctx.Err())\n\t\tcase <-time.After(time.Millisecond * 100):\n\t\t}\n\t})\n\n\tp2 := peer.AddrInfo{\n\t\tID:    \"peer2\",\n\t\tAddrs: []ma.Multiaddr{ma.StringCast(\"/ip6/::/tcp/1234\")},\n\t}\n\n\tt.Run(\"should update on new peer\", func(t *testing.T) {\n\t\tpc.UpdatePeer(topic, p2)\n\n\t\tvar updated []peer.ID\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\trequire.NoError(t, ctx.Err())\n\t\tcase updated = <-out:\n\t\t\trequire.Len(t, updated, 1)\n\t\t}\n\n\t\tps := pc.GetPeersForTopics(topic)\n\t\trequire.Len(t, ps, 2)\n\t})\n}\n"
  },
  {
    "path": "pkg/tinder/records.pb.go",
    "content": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.34.2\n// \tprotoc        (unknown)\n// source: tinder/records.proto\n\npackage tinder\n\nimport (\n\tprotoreflect \"google.golang.org/protobuf/reflect/protoreflect\"\n\tprotoimpl \"google.golang.org/protobuf/runtime/protoimpl\"\n\treflect \"reflect\"\n\tsync \"sync\"\n)\n\nconst (\n\t// Verify that this generated code is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)\n\t// Verify that runtime/protoimpl is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)\n)\n\ntype Records struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tRecords []*Record `protobuf:\"bytes,1,rep,name=records,proto3\" json:\"records,omitempty\"`\n}\n\nfunc (x *Records) Reset() {\n\t*x = Records{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_tinder_records_proto_msgTypes[0]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *Records) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Records) ProtoMessage() {}\n\nfunc (x *Records) ProtoReflect() protoreflect.Message {\n\tmi := &file_tinder_records_proto_msgTypes[0]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use Records.ProtoReflect.Descriptor instead.\nfunc (*Records) Descriptor() ([]byte, []int) {\n\treturn file_tinder_records_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *Records) GetRecords() []*Record {\n\tif x != nil {\n\t\treturn x.Records\n\t}\n\treturn nil\n}\n\ntype Record struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tCid    string `protobuf:\"bytes,1,opt,name=cid,proto3\" json:\"cid,omitempty\"`\n\tExpire int64  `protobuf:\"varint,2,opt,name=expire,proto3\" json:\"expire,omitempty\"`\n}\n\nfunc (x *Record) Reset() {\n\t*x = Record{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_tinder_records_proto_msgTypes[1]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *Record) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Record) ProtoMessage() {}\n\nfunc (x *Record) ProtoReflect() protoreflect.Message {\n\tmi := &file_tinder_records_proto_msgTypes[1]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use Record.ProtoReflect.Descriptor instead.\nfunc (*Record) Descriptor() ([]byte, []int) {\n\treturn file_tinder_records_proto_rawDescGZIP(), []int{1}\n}\n\nfunc (x *Record) GetCid() string {\n\tif x != nil {\n\t\treturn x.Cid\n\t}\n\treturn \"\"\n}\n\nfunc (x *Record) GetExpire() int64 {\n\tif x != nil {\n\t\treturn x.Expire\n\t}\n\treturn 0\n}\n\nvar File_tinder_records_proto protoreflect.FileDescriptor\n\nvar file_tinder_records_proto_rawDesc = []byte{\n\t0x0a, 0x14, 0x74, 0x69, 0x6e, 0x64, 0x65, 0x72, 0x2f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73,\n\t0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x74, 0x69, 0x6e, 0x64, 0x65, 0x72, 0x22, 0x33,\n\t0x0a, 0x07, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x12, 0x28, 0x0a, 0x07, 0x72, 0x65, 0x63,\n\t0x6f, 0x72, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x69, 0x6e,\n\t0x64, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x07, 0x72, 0x65, 0x63, 0x6f,\n\t0x72, 0x64, 0x73, 0x22, 0x32, 0x0a, 0x06, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x12, 0x10, 0x0a,\n\t0x03, 0x63, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x63, 0x69, 0x64, 0x12,\n\t0x16, 0x0a, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52,\n\t0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x42, 0x22, 0x5a, 0x20, 0x62, 0x65, 0x72, 0x74, 0x79,\n\t0x2e, 0x74, 0x65, 0x63, 0x68, 0x2f, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2f, 0x76, 0x32,\n\t0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x69, 0x6e, 0x64, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f,\n\t0x74, 0x6f, 0x33,\n}\n\nvar (\n\tfile_tinder_records_proto_rawDescOnce sync.Once\n\tfile_tinder_records_proto_rawDescData = file_tinder_records_proto_rawDesc\n)\n\nfunc file_tinder_records_proto_rawDescGZIP() []byte {\n\tfile_tinder_records_proto_rawDescOnce.Do(func() {\n\t\tfile_tinder_records_proto_rawDescData = protoimpl.X.CompressGZIP(file_tinder_records_proto_rawDescData)\n\t})\n\treturn file_tinder_records_proto_rawDescData\n}\n\nvar file_tinder_records_proto_msgTypes = make([]protoimpl.MessageInfo, 2)\nvar file_tinder_records_proto_goTypes = []any{\n\t(*Records)(nil), // 0: tinder.Records\n\t(*Record)(nil),  // 1: tinder.Record\n}\nvar file_tinder_records_proto_depIdxs = []int32{\n\t1, // 0: tinder.Records.records:type_name -> tinder.Record\n\t1, // [1:1] is the sub-list for method output_type\n\t1, // [1:1] is the sub-list for method input_type\n\t1, // [1:1] is the sub-list for extension type_name\n\t1, // [1:1] is the sub-list for extension extendee\n\t0, // [0:1] is the sub-list for field type_name\n}\n\nfunc init() { file_tinder_records_proto_init() }\nfunc file_tinder_records_proto_init() {\n\tif File_tinder_records_proto != nil {\n\t\treturn\n\t}\n\tif !protoimpl.UnsafeEnabled {\n\t\tfile_tinder_records_proto_msgTypes[0].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*Records); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_tinder_records_proto_msgTypes[1].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*Record); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n\ttype x struct{}\n\tout := protoimpl.TypeBuilder{\n\t\tFile: protoimpl.DescBuilder{\n\t\t\tGoPackagePath: reflect.TypeOf(x{}).PkgPath(),\n\t\t\tRawDescriptor: file_tinder_records_proto_rawDesc,\n\t\t\tNumEnums:      0,\n\t\t\tNumMessages:   2,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   0,\n\t\t},\n\t\tGoTypes:           file_tinder_records_proto_goTypes,\n\t\tDependencyIndexes: file_tinder_records_proto_depIdxs,\n\t\tMessageInfos:      file_tinder_records_proto_msgTypes,\n\t}.Build()\n\tFile_tinder_records_proto = out.File\n\tfile_tinder_records_proto_rawDesc = nil\n\tfile_tinder_records_proto_goTypes = nil\n\tfile_tinder_records_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "pkg/tinder/service.go",
    "content": "package tinder\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"sync\"\n\t\"sync/atomic\"\n\n\t\"github.com/libp2p/go-libp2p/core/host\"\n\t\"github.com/libp2p/go-libp2p/core/peer\"\n\t\"go.uber.org/zap\"\n\n\t\"berty.tech/weshnet/v2/pkg/logutil\"\n)\n\ntype Service struct {\n\thost          host.Host\n\tlogger        *zap.Logger\n\tdrivers       []IDriver\n\tnetworkNotify *NetworkUpdate\n\n\ttopicCounter map[string]*Subscription\n\tmuTopics     sync.Mutex\n\n\t// subscribe\n\tpeersCache *peersCache\n\tprocess    uint32\n}\n\nfunc NewService(h host.Host, logger *zap.Logger, drivers ...IDriver) (*Service, error) {\n\tnn, err := NewNetworkUpdate(logger, h)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to init service: %w\", err)\n\t}\n\n\treturn &Service{\n\t\thost:          h,\n\t\tlogger:        logger.Named(\"tinder\"),\n\t\tdrivers:       drivers,\n\t\tnetworkNotify: nn,\n\t\ttopicCounter:  make(map[string]*Subscription),\n\t\tpeersCache:    newPeerCache(),\n\t}, nil\n}\n\nfunc (s *Service) FindPeers(ctx context.Context, topic string) <-chan peer.AddrInfo {\n\ts.muTopics.Lock()\n\tdefer s.muTopics.Unlock()\n\n\tctx, cancel := context.WithCancel(ctx)\n\tgo func() {\n\t\tif err := s.LookupPeers(ctx, topic); err != nil {\n\t\t\ts.logger.Error(\"lookup failed\", logutil.PrivateString(\"topic\", topic), zap.Error(err))\n\t\t}\n\t\tcancel()\n\t}()\n\n\treturn s.fadeOut(ctx, topic, 16)\n}\n\n// Unregister try to unregister topic on each of his driver\nfunc (s *Service) Unregister(ctx context.Context, topic string) error {\n\tvar wg sync.WaitGroup\n\tvar success int32\n\n\tfor _, driver := range s.drivers {\n\t\twg.Add(1)\n\t\tgo func(driver IDriver) {\n\t\t\tif err := driver.Unregister(ctx, topic); err != nil {\n\t\t\t\ts.logger.Debug(\"unable to advertise\", zap.Error(err))\n\t\t\t} else {\n\t\t\t\tatomic.AddInt32(&success, 1)\n\t\t\t}\n\t\t\twg.Done()\n\t\t}(driver)\n\t}\n\n\twg.Wait()\n\n\tif success == 0 {\n\t\treturn fmt.Errorf(\"no driver(s) were available for unregister\")\n\t}\n\n\treturn nil\n}\n\nfunc (s *Service) WatchPeers(ctx context.Context, topic string) <-chan peer.AddrInfo {\n\treturn s.fadeOut(ctx, topic, 16)\n}\n\nfunc (s *Service) fadeIn(ctx context.Context, topic string, in <-chan peer.AddrInfo) {\n\ts.incProcess()\n\tdefer s.decProcess()\n\n\tfor {\n\t\tselect {\n\t\tcase p, ok := <-in:\n\t\t\tif !ok {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif updated := s.peersCache.UpdatePeer(topic, p); updated {\n\t\t\t\ts.logger.Debug(\"topic updated\", zap.String(\"topic\", topic), zap.String(\"peer\", p.String()))\n\t\t\t}\n\t\tcase <-ctx.Done():\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc (s *Service) fadeOut(ctx context.Context, topic string, bufsize int) <-chan peer.AddrInfo {\n\tout := make(chan peer.AddrInfo, bufsize)\n\n\tgo func() {\n\t\tknownPeers := make(PeersUpdate)\n\n\t\tfor {\n\t\t\t// Wait until `PeersCache` differ from `peerCache` peers status\n\t\t\tupdated, ok := s.peersCache.WaitForPeerUpdate(ctx, topic, knownPeers)\n\t\t\tif !ok {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\ts.logger.Debug(\"got update on peer\", zap.String(\"topic\", topic), zap.Int(\"peers\", len(updated)))\n\t\t\tfor _, peer := range s.peersCache.GetPeers(updated...) {\n\t\t\t\tout <- peer\n\t\t\t}\n\t\t}\n\n\t\t// we're done here, close the channel and decrement process\n\t\tclose(out)\n\t}()\n\n\treturn out\n}\n\nfunc (s *Service) Close() error {\n\treturn s.networkNotify.Close()\n}\n\nfunc (s *Service) GetProcess() uint32 { return atomic.LoadUint32(&s.process) }\nfunc (s *Service) incProcess()        { atomic.AddUint32(&s.process, 1) }\nfunc (s *Service) decProcess()        { atomic.AddUint32(&s.process, ^(uint32)(0)) }\n"
  },
  {
    "path": "pkg/tinder/service_adaptater.go",
    "content": "package tinder\n\nimport (\n\t\"context\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/libp2p/go-libp2p/core/discovery\"\n\t\"github.com/libp2p/go-libp2p/core/peer\"\n\t\"go.uber.org/zap\"\n\n\t\"berty.tech/weshnet/v2/pkg/logutil\"\n)\n\nvar _ discovery.Discovery = (*DiscoveryAdaptater)(nil)\n\ntype discoverySubscribtion struct {\n\tsub   *Subscription\n\ttimer *time.Timer\n}\n\ntype DiscoveryAdaptater struct {\n\tctx         context.Context\n\tcancel      context.CancelFunc\n\tlogger      *zap.Logger\n\tdefaultOpts []Option\n\n\tservice *Service\n\n\t// watchdogDiscover\n\twatchdogDiscover map[string]*discoverySubscribtion\n\tmuDiscover       sync.Mutex\n\n\t// advertise\n\twatchdogAdvertise map[string]*time.Timer\n\tmuAdvertiser      sync.Mutex\n\tresetInterval     time.Duration\n\tttl               time.Duration\n\n\tcloseOnce sync.Once\n}\n\nfunc NewDiscoveryAdaptater(logger *zap.Logger, service *Service, defaultOpts ...Option) *DiscoveryAdaptater {\n\tctx, cancel := context.WithCancel(context.Background())\n\treturn &DiscoveryAdaptater{\n\t\tctx:               ctx,\n\t\tcancel:            cancel,\n\t\tlogger:            logger.Named(\"disc\"),\n\t\twatchdogDiscover:  make(map[string]*discoverySubscribtion),\n\t\twatchdogAdvertise: make(map[string]*time.Timer),\n\t\tservice:           service,\n\t\tresetInterval:     time.Minute * 10,\n\t\tttl:               time.Minute * 5,\n\t\tdefaultOpts:       defaultOpts,\n\t}\n}\n\nfunc (a *DiscoveryAdaptater) FindPeers(_ context.Context, topic string, _ ...discovery.Option) (<-chan peer.AddrInfo, error) {\n\ta.muDiscover.Lock()\n\tdefer a.muDiscover.Unlock()\n\n\tif st, ok := a.watchdogDiscover[topic]; ok {\n\t\t// already running FindPeers, reset watchdog\n\t\tif !st.timer.Stop() {\n\t\t\t<-st.timer.C\n\t\t}\n\t\tst.timer.Reset(a.resetInterval)\n\n\t\t// watch again for new peers until the method expire\n\t\treturn st.sub.Out(), nil\n\t}\n\n\tstart := time.Now()\n\ta.logger.Debug(\"watchdogs looking for peers\", logutil.PrivateString(\"topic\", topic))\n\n\t// filter out local discovery\n\tsub := a.service.Subscribe(topic, a.defaultOpts...)\n\t// pull to fetch previous peers (FindPeers)\n\tgo func() {\n\t\tif err := sub.Pull(); err != nil {\n\t\t\ta.logger.Error(\"unable to pull topic\", zap.String(\"topic\", topic), zap.Error(err))\n\t\t}\n\t}()\n\n\t// create a new watchdog\n\ttimer := time.AfterFunc(a.resetInterval, func() {\n\t\ta.logger.Debug(\"findpeers expired\",\n\t\t\tlogutil.PrivateString(\"topic\", topic),\n\t\t\tzap.Duration(\"duration\", time.Since(start)))\n\n\t\t// watchdog has expired, cancel lookup+topic_watcher\n\t\ta.muDiscover.Lock()\n\t\tsub.Close()\n\t\tdelete(a.watchdogDiscover, topic)\n\t\ta.muDiscover.Unlock()\n\t})\n\n\ta.watchdogDiscover[topic] = &discoverySubscribtion{\n\t\ttimer: timer,\n\t\tsub:   sub,\n\t}\n\n\t// watch for new peers until method context expire\n\treturn sub.Out(), nil\n}\n\nfunc (a *DiscoveryAdaptater) Advertise(_ context.Context, topic string, _ ...discovery.Option) (time.Duration, error) {\n\tctx := a.ctx\n\n\ta.muAdvertiser.Lock()\n\tdefer a.muAdvertiser.Unlock()\n\n\tstart := time.Now()\n\tif t, ok := a.watchdogAdvertise[topic]; ok {\n\t\t// if we already advertise on this topic, reset the watchdog\n\t\tif !t.Stop() {\n\t\t\t<-t.C\n\t\t}\n\t\tt.Reset(a.resetInterval)\n\t} else {\n\t\twctx, cancel := context.WithCancel(ctx)\n\n\t\t// start advertising on this topic\n\t\t// filter out local discovery\n\t\tif err := a.service.StartAdvertises(wctx, topic, a.defaultOpts...); err != nil {\n\t\t\ta.logger.Error(\"advertise failed\", logutil.PrivateString(\"topic\", topic), zap.Error(err))\n\t\t\tcancel()\n\t\t\treturn time.Minute, err\n\t\t}\n\n\t\ta.logger.Debug(\"advertise started\", logutil.PrivateString(\"topic\", topic))\n\n\t\t// create a new watchdog\n\t\ta.watchdogAdvertise[topic] = time.AfterFunc(a.resetInterval, func() {\n\t\t\t// watchdog has expired, cancel advertise\n\n\t\t\ta.muAdvertiser.Lock()\n\t\t\tcancel()\n\t\t\ta.logger.Debug(\"advertise expired\",\n\t\t\t\tlogutil.PrivateString(\"topic\", topic),\n\t\t\t\tzap.Duration(\"duration\", time.Since(start)),\n\t\t\t)\n\n\t\t\tdelete(a.watchdogAdvertise, topic)\n\t\t\ta.muAdvertiser.Unlock()\n\n\t\t\t// unregister from this topic if possible\n\t\t\tif err := a.service.Unregister(ctx, topic); err != nil {\n\t\t\t\ta.logger.Debug(\"unregister failed\",\n\t\t\t\t\tlogutil.PrivateString(\"topic\", topic),\n\t\t\t\t\tzap.Error(err),\n\t\t\t\t)\n\t\t\t}\n\t\t})\n\t}\n\n\treturn a.ttl, nil\n}\n\nfunc (a *DiscoveryAdaptater) Close() error {\n\ta.closeOnce.Do(func() {\n\t\ta.muDiscover.Lock()\n\t\ta.cancel()\n\t\tfor _, st := range a.watchdogDiscover {\n\t\t\tif !st.timer.Stop() {\n\t\t\t\t<-st.timer.C\n\t\t\t}\n\t\t\t_ = st.sub.Close()\n\t\t}\n\t\ta.muDiscover.Unlock()\n\n\t\ta.muAdvertiser.Lock()\n\t\tfor _, t := range a.watchdogAdvertise {\n\t\t\tif !t.Stop() {\n\t\t\t\t<-t.C\n\t\t\t}\n\t\t}\n\t\ta.muAdvertiser.Unlock()\n\t})\n\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/tinder/service_advertises.go",
    "content": "package tinder\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"go.uber.org/zap\"\n\n\t\"berty.tech/weshnet/v2/pkg/logutil\"\n)\n\nconst defaultTTL = time.Hour\n\n// StartAdvertises topic on each of service drivers\nfunc (s *Service) StartAdvertises(ctx context.Context, topic string, opts ...Option) error {\n\tif len(s.drivers) == 0 {\n\t\treturn fmt.Errorf(\"no driver available to advertise\")\n\t}\n\n\tvar aopts Options\n\tif err := aopts.apply(opts...); err != nil {\n\t\treturn fmt.Errorf(\"failed to advertise: %w\", err)\n\t}\n\n\tfor _, driver := range s.drivers {\n\t\t// skip filter driver\n\t\tif aopts.DriverFilters.ShouldFilter(driver.Name()) {\n\t\t\tcontinue\n\t\t}\n\n\t\t// start background job\n\t\tgo func(driver IDriver) {\n\t\t\tif err := s.advertise(ctx, driver, topic); err != nil {\n\t\t\t\ts.logger.Debug(\"advertise ended\", zap.Error(err))\n\t\t\t}\n\t\t}(driver)\n\t}\n\n\treturn nil\n}\n\nfunc (s *Service) advertise(ctx context.Context, d IDriver, topic string) error {\n\tfor {\n\t\tcurrentAddrs := s.networkNotify.GetLastUpdatedAddrs(ctx)\n\n\t\tnow := time.Now()\n\t\tttl, err := d.Advertise(ctx, topic)\n\t\ttook := time.Since(now)\n\n\t\tvar deadline time.Duration\n\t\tif err != nil {\n\t\t\tselect {\n\t\t\tcase <-ctx.Done():\n\t\t\t\treturn ctx.Err()\n\t\t\tdefault:\n\t\t\t}\n\n\t\t\t// retry in 30 seconds\n\t\t\tdeadline = time.Second * 30\n\t\t} else {\n\t\t\tif ttl == 0 {\n\t\t\t\tttl = defaultTTL\n\t\t\t}\n\t\t\tdeadline = 4 * ttl / 5\n\t\t}\n\n\t\ts.logger.Debug(\"advertise\",\n\t\t\tzap.String(\"driver\", d.Name()),\n\t\t\tlogutil.PrivateString(\"topic\", topic),\n\t\t\tzap.Duration(\"ttl\", ttl),\n\t\t\tzap.Duration(\"took\", took),\n\t\t\tzap.Duration(\"next\", deadline),\n\t\t\tzap.Error(err),\n\t\t)\n\n\t\twaitctx, cancel := context.WithTimeout(ctx, deadline)\n\t\t// wait for network update or waitctx expire\n\t\t_, ok := s.networkNotify.WaitForUpdate(waitctx, currentAddrs)\n\t\tcancel()\n\n\t\t// filter addrs\n\n\t\tif !ok {\n\t\t\tselect {\n\t\t\t// check for parent ctx\n\t\t\tcase <-ctx.Done():\n\t\t\t\t// main context has expire, stop\n\t\t\t\treturn ctx.Err()\n\t\t\tdefault:\n\t\t\t\t// waitContext has expire, continue\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "pkg/tinder/service_mocked_test.go",
    "content": "package tinder\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/libp2p/go-libp2p/core/host\"\n\t\"github.com/libp2p/go-libp2p/core/peer\"\n\tmocknet \"github.com/libp2p/go-libp2p/p2p/net/mock\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/zap\"\n\n\t\"berty.tech/weshnet/v2/pkg/testutil\"\n)\n\nfunc TestMockedServiceSubscribeMultipleDriver(t *testing.T) {\n\tconst nPeers = 100\n\tconst topic = \"test_topic\"\n\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\tmn := mocknet.New()\n\tdefer mn.Close()\n\n\tcases := []struct {\n\t\tNDriver, NPeersPerDriver int\n\t}{\n\t\t{1, 1},\n\t\t{1, 10},\n\t\t{1, 100},\n\t\t{100, 1},\n\t\t{10, 10},\n\t}\n\n\tfor _, tc := range cases {\n\t\tname := fmt.Sprintf(\"%d_dirvers-%d_peer_per_driver\", tc.NDriver, tc.NPeersPerDriver)\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\trootp, err := mn.GenPeer()\n\t\t\trequire.NoError(t, err)\n\n\t\t\tclients := make([]IDriver, tc.NDriver*tc.NPeersPerDriver)\n\t\t\tdrivers := make([]IDriver, tc.NDriver)\n\t\t\tfor i := 0; i < tc.NDriver; i++ {\n\t\t\t\tsrv := NewMockDriverServer()\n\t\t\t\tdrivers[i] = srv.Client(rootp)\n\t\t\t\tfor j := 0; j < tc.NPeersPerDriver; j++ {\n\t\t\t\t\tp, err := mn.GenPeer()\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tindex := tc.NPeersPerDriver*i + j\n\t\t\t\t\tclients[index] = srv.Client(p)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tservice, err := NewService(rootp, zap.NewNop(), drivers...)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tfor _, client := range clients {\n\t\t\t\t_, err := client.Advertise(ctx, topic)\n\t\t\t\tassert.NoError(t, err)\n\t\t\t}\n\n\t\t\tsub := service.Subscribe(topic)\n\t\t\tdefer sub.Close()\n\n\t\t\terr = sub.Pull()\n\t\t\trequire.NoError(t, err)\n\n\t\t\tseen := make(map[peer.ID]struct{})\n\t\t\tvar count int\n\t\t\tfor count = 0; count < len(clients); count++ {\n\t\t\t\tselect {\n\t\t\t\tcase p := <-sub.Out():\n\t\t\t\t\tif _, ok := seen[p.ID]; ok {\n\t\t\t\t\t\trequire.FailNow(t, \"should only receive a peer once, received\")\n\t\t\t\t\t}\n\t\t\t\t\tseen[p.ID] = struct{}{}\n\n\t\t\t\tcase <-time.After(time.Second):\n\t\t\t\t\trequire.FailNow(t, \"timeout while waiting for peer\", \"received: %d\", count)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tassert.Equal(t, tc.NDriver*tc.NPeersPerDriver, count)\n\t\t})\n\t}\n}\n\nfunc TestMockedServiceSubscribePull(t *testing.T) {\n\tconst topic = \"test_topic\"\n\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\tlogger, cleanup := testutil.Logger(t)\n\tdefer cleanup()\n\n\tmn := mocknet.New()\n\tdefer mn.Close()\n\n\tsrv := NewMockDriverServer()\n\n\tt.Run(\"with pull\", func(t *testing.T) {\n\t\tconst topic = \"test_topic_1\"\n\t\tp1, s1 := newTestMockedService(t, logger, mn, srv)\n\t\t_, s2 := newTestMockedService(t, logger, mn, srv)\n\n\t\terr := s1.StartAdvertises(ctx, topic)\n\t\trequire.NoError(t, err)\n\n\t\terr = srv.WaitForPeer(topic, p1.ID(), time.Second)\n\t\trequire.NoError(t, err)\n\n\t\tsub := s2.Subscribe(topic)\n\t\tdefer sub.Close()\n\n\t\terr = sub.Pull()\n\t\trequire.NoError(t, err)\n\n\t\tselect {\n\t\tcase p := <-sub.Out():\n\t\t\tassert.Equal(t, p1.ID(), p.ID)\n\t\t\tassert.Equal(t, p1.Addrs(), p.Addrs)\n\t\tcase <-time.After(time.Second * 2):\n\t\t\trequire.FailNow(t, \"timeout while waiting for peer\")\n\t\t}\n\t})\n\n\tt.Run(\"no pull\", func(t *testing.T) {\n\t\tconst topic = \"test_topic_2\"\n\n\t\tp1, s1 := newTestMockedService(t, logger, mn, srv)\n\t\t_, s2 := newTestMockedService(t, logger, mn, srv)\n\n\t\terr := s1.StartAdvertises(ctx, topic)\n\t\trequire.NoError(t, err)\n\n\t\terr = srv.WaitForPeer(topic, p1.ID(), time.Second)\n\t\trequire.NoError(t, err)\n\n\t\tsub := s2.Subscribe(topic)\n\t\tdefer sub.Close()\n\n\t\tselect {\n\t\tcase <-sub.Out():\n\t\t\trequire.FailNow(t, \"do no expect peers\")\n\t\tcase <-time.After(time.Millisecond * 500):\n\t\t}\n\t})\n}\n\nfunc TestMockedServiceSubscribeDuplicatePeer(t *testing.T) {\n\tconst topic = \"test_topic\"\n\tconst NServers = 10\n\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\tlogger, cleanup := testutil.Logger(t)\n\tdefer cleanup()\n\n\tmn := mocknet.New()\n\tdefer mn.Close()\n\n\tservers := make([]*MockDriverServer, NServers)\n\tfor i := range servers {\n\t\tservers[i] = NewMockDriverServer()\n\t}\n\n\tp1, s1 := newTestMockedService(t, logger, mn, servers...)\n\t_, s2 := newTestMockedService(t, logger, mn, servers...)\n\n\terr := s1.StartAdvertises(ctx, topic)\n\trequire.NoError(t, err)\n\n\tfor _, s := range servers {\n\t\terr := s.WaitForPeer(topic, p1.ID(), time.Second)\n\t\trequire.NoError(t, err)\n\t}\n\n\tsub := s2.Subscribe(topic)\n\tdefer sub.Close()\n\n\terr = sub.Pull()\n\trequire.NoError(t, err)\n\n\tvar count int\n\tfor {\n\t\tselect {\n\t\tcase p := <-sub.Out():\n\t\t\tassert.Equal(t, p1.ID(), p.ID)\n\t\t\tassert.Equal(t, p1.Addrs(), p.Addrs)\n\t\t\tcount++\n\t\tcase <-time.After(time.Millisecond * 500):\n\t\t\trequire.Equal(t, 1, count)\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc newTestMockedService(t *testing.T, logger *zap.Logger, mn mocknet.Mocknet, srvs ...*MockDriverServer) (host.Host, *Service) {\n\tt.Helper()\n\n\th, err := mn.GenPeer()\n\trequire.NoError(t, err)\n\n\tdrivers := make([]IDriver, len(srvs))\n\tfor i, srv := range srvs {\n\t\tdrivers[i] = srv.Client(h)\n\t}\n\n\ts, err := NewService(h, logger, drivers...)\n\trequire.NoError(t, err)\n\n\treturn h, s\n}\n"
  },
  {
    "path": "pkg/tinder/service_subscription.go",
    "content": "package tinder\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/libp2p/go-libp2p/core/peer\"\n\t\"go.uber.org/zap\"\n)\n\ntype Subscription struct {\n\tcancel  context.CancelFunc\n\tctx     context.Context\n\tservice *Service\n\ttopic   string\n\tout     <-chan peer.AddrInfo\n\topts    []Option\n}\n\nfunc (s *Subscription) Out() <-chan peer.AddrInfo {\n\treturn s.out\n}\n\nfunc (s *Subscription) Pull() error {\n\treturn s.service.LookupPeers(s.ctx, s.topic, s.opts...)\n}\n\nfunc (s *Subscription) Close() error {\n\ts.cancel()\n\treturn nil\n}\n\nfunc (s *Service) Subscribe(topic string, opts ...Option) *Subscription {\n\tctx, cancel := context.WithCancel(context.Background())\n\tout := s.fadeOut(ctx, topic, 16)\n\terr := s.WatchTopic(ctx, topic, opts...)\n\tif err != nil {\n\t\ts.logger.Warn(\"unable to watch topic\", zap.String(\"topic\", topic), zap.Error(err))\n\t}\n\n\treturn &Subscription{\n\t\tservice: s,\n\t\tout:     out,\n\t\tctx:     ctx,\n\t\tcancel:  cancel,\n\t\ttopic:   topic,\n\t\topts:    opts,\n\t}\n}\n\nfunc (s *Service) LookupPeers(ctx context.Context, topic string, opts ...Option) error {\n\tvar success int\n\n\tvar aopts Options\n\tif err := aopts.apply(opts...); err != nil {\n\t\treturn fmt.Errorf(\"unable to apply option: %w\", err)\n\t}\n\n\tfor _, d := range s.drivers {\n\t\tif aopts.DriverFilters.ShouldFilter(d.Name()) {\n\t\t\tcontinue\n\t\t}\n\n\t\tin, err := d.FindPeers(ctx, topic) // find peers should not hang there\n\t\tswitch err {\n\t\tcase nil: // ok\n\t\t\tsuccess++\n\t\t\ts.logger.Debug(\"lookup for topic started\", zap.String(\"driver\", d.Name()), zap.String(\"topic\", topic))\n\t\t\tgo s.fadeIn(ctx, topic, in)\n\t\tcase ErrNotSupported: // do nothing\n\t\tdefault:\n\t\t\ts.logger.Error(\"lookup failed\",\n\t\t\t\tzap.String(\"driver\", d.Name()), zap.String(\"topic\", topic), zap.Error(err))\n\t\t}\n\t}\n\n\tif success == 0 {\n\t\treturn fmt.Errorf(\"no driver(s) were available for lookup\")\n\t}\n\n\treturn nil\n}\n\nfunc (s *Service) WatchTopic(ctx context.Context, topic string, opts ...Option) (err error) {\n\tvar success int\n\n\tvar aopts Options\n\tif err := aopts.apply(opts...); err != nil {\n\t\treturn fmt.Errorf(\"unable to apply option: %w\", err)\n\t}\n\n\tfor _, d := range s.drivers {\n\t\tif aopts.DriverFilters.ShouldFilter(d.Name()) {\n\t\t\tcontinue\n\t\t}\n\n\t\ts.logger.Debug(\"start subscribe\", zap.String(\"driver\", d.Name()), zap.String(\"topic\", topic))\n\n\t\tin, err := d.Subscribe(ctx, topic)\n\t\tswitch err {\n\t\tcase nil: // ok, start fadin\n\t\t\tsuccess++\n\t\t\ts.logger.Debug(\"watching for topic update\", zap.String(\"driver\", d.Name()), zap.String(\"topic\", topic))\n\n\t\t\tgo s.fadeIn(ctx, topic, in)\n\t\tcase ErrNotSupported: // not, supported skipping\n\t\tdefault:\n\t\t\ts.logger.Error(\"unable to subscribe on topic\",\n\t\t\t\tzap.String(\"driver\", d.Name()), zap.String(\"topic\", topic), zap.Error(err))\n\t\t}\n\t}\n\n\tif success == 0 {\n\t\terr = fmt.Errorf(\"no driver(s) were available for subscribe\")\n\t}\n\n\treturn err\n}\n"
  },
  {
    "path": "pkg/tinder/testing_test.go",
    "content": "package tinder\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\trendezvous \"github.com/berty/go-libp2p-rendezvous\"\n\tdbrdvp \"github.com/berty/go-libp2p-rendezvous/db/sqlite\"\n\tp2putil \"github.com/libp2p/go-libp2p-testing/netutil\"\n\t\"github.com/libp2p/go-libp2p/core/host\"\n\t\"github.com/libp2p/go-libp2p/core/peer\"\n\tmocknet \"github.com/libp2p/go-libp2p/p2p/net/mock\"\n\tma \"github.com/multiformats/go-multiaddr\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nvar (\n\tErrChannelNotEmpty = fmt.Errorf(\"channel not empty\")\n\tErrChannelTimeout  = fmt.Errorf(\"waiting for channel: timeout\")\n)\n\nfunc makeRendezvousService(t *testing.T, mn mocknet.Mocknet) (target peer.ID, svc *rendezvous.RendezvousService) {\n\tctx, cancel := context.WithCancel(context.Background())\n\tt.Cleanup(cancel)\n\n\tpeer, err := mn.GenPeer()\n\trequire.NoError(t, err)\n\n\tpubsubsync, err := rendezvous.NewSyncInMemProvider(peer)\n\trequire.NoError(t, err)\n\n\tdbi, err := dbrdvp.OpenDB(ctx, \":memory:\")\n\trequire.NoError(t, err)\n\tt.Cleanup(func() { _ = dbi.Close() })\n\n\treturn peer.ID(), rendezvous.NewRendezvousService(peer, dbi, pubsubsync)\n}\n\nfunc testWaitForPeers(t *testing.T, out <-chan peer.AddrInfo, timeout time.Duration) (*peer.AddrInfo, error) {\n\tt.Helper()\n\n\tselect {\n\tcase p := <-out:\n\t\treturn &p, nil\n\tcase <-time.After(timeout):\n\t\treturn nil, fmt.Errorf(\"timeout while waiting for peer\")\n\t}\n}\n\nfunc testDrainChannel(t *testing.T, out <-chan peer.AddrInfo, timeout time.Duration) error {\n\tt.Helper()\n\n\tfor {\n\t\tselect {\n\t\tcase _, ok := <-out:\n\t\t\tif !ok {\n\t\t\t\treturn nil // ok\n\t\t\t}\n\n\t\t\treturn fmt.Errorf(\"channel wasn't empty\")\n\t\tcase <-time.After(timeout):\n\t\t\treturn fmt.Errorf(\"timeout while waiting for peer\")\n\t\t}\n\t}\n}\n\nfunc testPeersChanToSlice(t *testing.T, out <-chan peer.AddrInfo) (peers []*peer.AddrInfo) {\n\tt.Helper()\n\tpeers = []*peer.AddrInfo{}\n\n\tfor p := range out {\n\t\tpeers = append(peers, &p)\n\t}\n\n\treturn\n}\n\nfunc genLocalPeer(t *testing.T, mn mocknet.Mocknet) host.Host {\n\tsk, err := p2putil.RandTestBogusPrivateKey()\n\trequire.NoError(t, err)\n\n\ta, err := ma.NewMultiaddr(\"/ip4/127.0.0.1/tcp/0\")\n\trequire.NoError(t, err)\n\n\th, err := mn.AddPeer(sk, a)\n\trequire.NoError(t, err)\n\n\treturn h\n}\n"
  },
  {
    "path": "pkg/tyber/context.go",
    "content": "package tyber\n\nimport (\n\t\"context\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"google.golang.org/grpc/metadata\"\n)\n\ntype traceIDKeyType string\n\nconst (\n\ttraceIDKey   traceIDKeyType = \"TyberTraceID\"\n\tnoTraceID    string         = \"no_trace_id\"\n\tuuidFallback string         = \"409123fa-4dd5-11eb-a4a1-675173c25b22\"\n)\n\nvar (\n\tNewSessionID = newID\n\tNewTraceID   = newID\n)\n\nfunc ContextWithTraceID(ctx context.Context) (context.Context, bool) {\n\tif eid := GetTraceIDFromContext(ctx); eid != noTraceID {\n\t\treturn ctx, false\n\t}\n\tif md, ok := metadata.FromIncomingContext(ctx); ok {\n\t\tif vals := md.Get(string(traceIDKey)); len(vals) != 0 {\n\t\t\treturn ContextWithConstantTraceID(ctx, vals[0]), false\n\t\t}\n\t}\n\treturn ContextWithConstantTraceID(ctx, NewTraceID()), true\n}\n\nfunc ContextWithConstantTraceID(ctx context.Context, traceID string) context.Context {\n\tmd, ok := metadata.FromOutgoingContext(ctx)\n\tif ok {\n\t\tmd = md.Copy()\n\t} else {\n\t\tmd = metadata.New(nil)\n\t}\n\tmd.Set(string(traceIDKey), traceID)\n\treturn metadata.NewOutgoingContext(context.WithValue(ctx, traceIDKey, traceID), md)\n}\n\nfunc GetTraceIDFromContext(ctx context.Context) string {\n\tif val, ok := ctx.Value(traceIDKey).(string); ok {\n\t\treturn val\n\t}\n\treturn noTraceID\n}\n\nfunc ContextWithoutTraceID(ctx context.Context) context.Context {\n\treturn ContextWithConstantTraceID(ctx, noTraceID)\n}\n\nfunc newID() string {\n\tuid, err := uuid.NewV4()\n\t// If error while reading random, fallback on uuid v5\n\tif err != nil {\n\t\tns, err := uuid.FromString(uuidFallback)\n\t\tif err != nil {\n\t\t\tpanic(err) // Should never happen\n\t\t}\n\t\tn := strconv.FormatInt(time.Now().UnixNano(), 10)\n\t\tuid = uuid.NewV5(ns, n)\n\t}\n\n\treturn uid.String()\n}\n"
  },
  {
    "path": "pkg/tyber/format.go",
    "content": "package tyber\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"slices\"\n\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\ntype Detail struct {\n\tName        string `json:\"name\"`\n\tDescription string `json:\"description\"`\n}\n\nfunc safeJSONMarshal(v any) string {\n\tbs, err := json.MarshalIndent(v, \"\", \"    \")\n\tif err != nil {\n\t\treturn fmt.Sprintf(`\"%s\"`, err.Error())\n\t}\n\treturn string(bs)\n}\n\nfunc JSONDetail(name string, val any) Detail {\n\treturn Detail{Name: name, Description: safeJSONMarshal(val)}\n}\n\nfunc WithJSONDetail(name string, val any) StepMutator {\n\treturn func(s Step) Step {\n\t\ts.Details = append(s.Details, JSONDetail(name, val))\n\t\treturn s\n\t}\n}\n\nfunc WithError(err error) StepMutator {\n\treturn func(s Step) Step {\n\t\ts.Details = append(s.Details, Detail{Name: \"Error\", Description: err.Error()})\n\t\treturn s\n\t}\n}\n\ntype LogType string\n\nconst (\n\tTraceType     LogType = \"trace\"\n\tStepType      LogType = \"step\"\n\tEventType     LogType = \"event\"\n\tSubscribeType LogType = \"subcribe\"\n)\n\nvar KnownLogTypes = []LogType{TraceType, StepType, EventType, SubscribeType}\n\nfunc (lt LogType) IsKnown() bool {\n\treturn slices.Contains(KnownLogTypes, lt)\n}\n\ntype StatusType string\n\nconst (\n\tRunning   StatusType = \"running\"\n\tSucceeded StatusType = \"succeeded\"\n\tFailed    StatusType = \"failed\"\n)\n\ntype Trace struct {\n\tTraceID string `json:\"traceID\"`\n}\n\ntype Event struct {\n\tDetails []Detail `json:\"details\"`\n}\n\nfunc FormatTraceLogFields(ctx context.Context) []zapcore.Field {\n\treturn []zapcore.Field{\n\t\tzap.String(\"tyberLogType\", string(TraceType)),\n\t\tzap.Any(\"trace\", Trace{\n\t\t\tTraceID: GetTraceIDFromContext(ctx),\n\t\t}),\n\t}\n}\n\nfunc FormatEventLogFields(_ context.Context, details []Detail) []zapcore.Field {\n\treturn []zapcore.Field{\n\t\tzap.String(\"tyberLogType\", string(EventType)),\n\t\tzap.Any(\"event\", Event{\n\t\t\tDetails: details,\n\t\t}),\n\t}\n}\n\nfunc ZapFieldsToDetails(fields ...zap.Field) []Detail {\n\tdets := make([]Detail, len(fields))\n\tfor i, field := range fields {\n\t\tdets[i] = Detail{Name: field.Key, Description: field.String}\n\t}\n\treturn dets\n}\n"
  },
  {
    "path": "pkg/tyber/ipfs.go",
    "content": "package tyber\n\nimport (\n\tipfscid \"github.com/ipfs/go-cid\"\n)\n\nfunc WithCIDDetail(name string, cidBytes []byte) StepMutator {\n\tcid, err := ipfscid.Cast(cidBytes)\n\tif err != nil {\n\t\treturn func(s Step) Step { return s }\n\t}\n\treturn func(s Step) Step {\n\t\ts.Details = append(s.Details, Detail{Name: name, Description: cid.String()})\n\t\treturn s\n\t}\n}\n"
  },
  {
    "path": "pkg/tyber/log.go",
    "content": "package tyber\n\nimport (\n\t\"context\"\n\n\t\"go.uber.org/zap\"\n)\n\nconst tyberLogNS = \"tyber\"\n\nfunc LogError(ctx context.Context, logger *zap.Logger, text string, err error, mutators ...StepMutator) error {\n\tif logger == nil {\n\t\treturn err\n\t}\n\n\tlogger.Named(tyberLogNS).Error(\n\t\ttext,\n\t\tFormatStepLogFields(ctx, []Detail{{Name: \"Error\", Description: err.Error()}}, mutators...)...,\n\t)\n\n\t// returning the input error for better usage syntax\n\treturn err\n}\n\nfunc LogFatalError(ctx context.Context, logger *zap.Logger, text string, err error, mutators ...StepMutator) error {\n\treturn LogError(ctx, logger, text, err, append(mutators, Fatal)...)\n}\n\nfunc LogTraceEnd(ctx context.Context, logger *zap.Logger, text string, mutators ...StepMutator) {\n\tlogger.Named(tyberLogNS).Debug(text, FormatStepLogFields(ctx, []Detail{}, append(mutators, EndTrace)...)...)\n}\n\nfunc LogTraceStart(ctx context.Context, logger *zap.Logger, text string) {\n\tlogger.Named(tyberLogNS).Debug(text, FormatTraceLogFields(ctx)...)\n}\n\nfunc LogStep(ctx context.Context, logger *zap.Logger, text string, mutators ...StepMutator) {\n\tlogger.Named(tyberLogNS).Debug(text, FormatStepLogFields(ctx, []Detail{}, mutators...)...)\n}\n"
  },
  {
    "path": "pkg/tyber/section.go",
    "content": "package tyber\n\nimport (\n\t\"context\"\n\n\t\"go.uber.org/zap\"\n)\n\nfunc Section(ctx context.Context, logger *zap.Logger, name string) (context.Context, bool, func(error, string, ...StepMutator)) {\n\tctx, newTrace := ContextWithTraceID(ctx)\n\tif newTrace {\n\t\tLogTraceStart(ctx, logger, name)\n\t} else {\n\t\tLogStep(ctx, logger, name)\n\t}\n\treturn ctx, newTrace, func(err error, rename string, muts ...StepMutator) {\n\t\tif err != nil {\n\t\t\terrorName := \"Error while \" + name\n\t\t\tif rename != \"\" {\n\t\t\t\terrorName = rename\n\t\t\t}\n\t\t\tif newTrace {\n\t\t\t\t_ = LogFatalError(ctx, logger, errorName, err, muts...)\n\t\t\t} else {\n\t\t\t\t_ = LogError(ctx, logger, errorName, err, muts...)\n\t\t\t}\n\t\t} else {\n\t\t\tsuccessName := name + \" succeeded\"\n\t\t\tif rename != \"\" {\n\t\t\t\tsuccessName = rename\n\t\t\t}\n\t\t\tif newTrace {\n\t\t\t\tLogTraceEnd(ctx, logger, successName, muts...)\n\t\t\t} else {\n\t\t\t\tLogStep(ctx, logger, successName, muts...)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc SimpleSection(ctx context.Context, logger *zap.Logger, name string) func(error, ...StepMutator) {\n\t_, _, end := Section(ctx, logger, name)\n\treturn func(err error, muts ...StepMutator) { end(err, \"\", muts...) }\n}\n"
  },
  {
    "path": "pkg/tyber/step.go",
    "content": "package tyber\n\nimport (\n\t\"context\"\n\t\"runtime/debug\"\n\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\n// types\n\ntype Step struct {\n\tParentTraceID   string     `json:\"parentTraceID\"`\n\tDetails         []Detail   `json:\"details\"`\n\tStatus          StatusType `json:\"status\"`\n\tEndTrace        bool       `json:\"endTrace\"`\n\tUpdateTraceName string     `json:\"updateTraceName\"`\n\tForceReopen     bool       `json:\"forceReopen\"`\n}\n\ntype StepMutator = func(Step) Step\n\n// zap format\n\nfunc FormatStepLogFields(ctx context.Context, details []Detail, mutators ...StepMutator) []zapcore.Field {\n\ts := Step{\n\t\tParentTraceID: GetTraceIDFromContext(ctx),\n\t\tStatus:        Succeeded,\n\t\tDetails:       details,\n\t\tEndTrace:      false,\n\t\tForceReopen:   false,\n\t}\n\tfor _, m := range mutators {\n\t\ts = m(s)\n\t}\n\n\t// Add debug if a there is no parent trace ID\n\tif s.ParentTraceID == noTraceID {\n\t\ts.Details = append(s.Details, Detail{Name: \"StackTrace\", Description: string(debug.Stack())})\n\t}\n\n\treturn []zapcore.Field{\n\t\tzap.String(\"tyberLogType\", string(StepType)),\n\t\tzap.Any(\"step\", s),\n\t}\n}\n\n// constant mutators\n\nfunc ForceReopen(s Step) Step {\n\ts.ForceReopen = true\n\treturn s\n}\n\nfunc EndTrace(s Step) Step {\n\ts.EndTrace = true\n\treturn s\n}\n\nfunc Fatal(s Step) Step {\n\ts.EndTrace = true\n\ts.Status = Failed\n\treturn s\n}\n\n// variable mutators\n\nfunc Status(st StatusType) StepMutator {\n\treturn func(s Step) Step {\n\t\ts.Status = st\n\t\treturn s\n\t}\n}\n\nfunc UpdateTraceName(newTitle string) StepMutator {\n\treturn func(s Step) Step {\n\t\ts.UpdateTraceName = newTitle\n\t\treturn s\n\t}\n}\n\nfunc WithDetail(name string, description string) StepMutator {\n\treturn func(s Step) Step {\n\t\ts.Details = append(s.Details, Detail{Name: name, Description: description})\n\t\treturn s\n\t}\n}\n"
  },
  {
    "path": "pkg/tyber/subscribe.go",
    "content": "package tyber\n\nimport (\n\t\"context\"\n\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\ntype Subscribe struct {\n\tTargetStepName string   `json:\"targetStepName\"`\n\tTargetDetails  []Detail `json:\"targetDetails\"`\n\tStepToAdd      Step     `json:\"stepToAdd\"`\n}\n\nfunc FormatSubscribeLogFields(ctx context.Context, targetName string, targetDetails []Detail, stepToAddMutators ...StepMutator) []zapcore.Field {\n\tsta := Step{\n\t\tParentTraceID: GetTraceIDFromContext(ctx),\n\t\tStatus:        Succeeded,\n\t}\n\tfor _, m := range stepToAddMutators {\n\t\tsta = m(sta)\n\t}\n\treturn []zapcore.Field{\n\t\tzap.String(\"tyberLogType\", string(SubscribeType)),\n\t\tzap.Any(\"subscribe\", Subscribe{\n\t\t\tTargetStepName: targetName,\n\t\t\tTargetDetails:  targetDetails,\n\t\t\tStepToAdd:      sta,\n\t\t}),\n\t}\n}\n"
  },
  {
    "path": "pkg/username/android.go",
    "content": "//go:build android\n\npackage username\n\n/*\n#include <sys/system_properties.h>\n#include <string.h>\n#include <stdlib.h>\n\n// Could be improved using Android Java API\n// https://medium.com/capital-one-tech/how-to-get-an-android-device-nickname-d5eab12f4ced\n\nconst char* GetDeviceName() {\n\tchar model[PROP_VALUE_MAX + 1];\n\tint len = __system_property_get(\"ro.product.model\", model);\n\tchar *name = malloc(len + 1);\n\n\tmemcpy(name, model, len);\n\tname[len] = 0;\n\n\treturn name;\n}\n*/\nimport \"C\"\n\nimport \"unsafe\"\n\nconst defaultUsername = \"android#1337\"\n\nfunc getUsername() string {\n\tcstring := C.GetDeviceName()\n\tusername := C.GoString(cstring)\n\tC.free(unsafe.Pointer(cstring))\n\n\treturn username\n}\n"
  },
  {
    "path": "pkg/username/example_test.go",
    "content": "package username_test\n"
  },
  {
    "path": "pkg/username/ios.go",
    "content": "//go:build ios && !catalyst\n\npackage username\n\n/*\n#cgo CFLAGS: -x objective-c\n#cgo darwin LDFLAGS: -framework Foundation -framework UIKit\n#import <Foundation/Foundation.h>\n#import <UIKit/UIKit.h>\n\nconst char* GetDeviceName() {\n\tNSString *deviceName = [[UIDevice currentDevice] name];\n\tchar *copy = strdup([deviceName UTF8String]);\n\n\treturn copy;\n}\n*/\nimport \"C\"\n\nimport \"unsafe\"\n\nconst defaultUsername = \"ios#1337\"\n\nfunc getUsername() string {\n\tcstring := C.GetDeviceName()\n\tusername := C.GoString(cstring)\n\tC.free(unsafe.Pointer(cstring))\n\n\treturn username\n}\n"
  },
  {
    "path": "pkg/username/others.go",
    "content": "//go:build (!android && !ios) || (ios && catalyst)\n\npackage username\n\nimport \"os/user\"\n\nconst defaultUsername = \"anon#1337\"\n\nfunc getUsername() string {\n\tuser, err := user.Current()\n\tif err != nil || user == nil {\n\t\treturn \"\"\n\t}\n\n\treturn user.Name\n}\n"
  },
  {
    "path": "pkg/username/username.go",
    "content": "package username\n\nimport \"strings\"\n\nfunc GetUsername() string {\n\tusername := getUsername()\n\ttrimed := strings.TrimSpace(username)\n\tif trimed != \"\" {\n\t\treturn trimed\n\t}\n\n\treturn defaultUsername\n}\n"
  },
  {
    "path": "pkg/verifiablecredstypes/bertyverifiablecreds.pb.go",
    "content": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.34.2\n// \tprotoc        (unknown)\n// source: verifiablecredstypes/bertyverifiablecreds.proto\n\npackage verifiablecredstypes\n\nimport (\n\tprotoreflect \"google.golang.org/protobuf/reflect/protoreflect\"\n\tprotoimpl \"google.golang.org/protobuf/runtime/protoimpl\"\n\treflect \"reflect\"\n\tsync \"sync\"\n)\n\nconst (\n\t// Verify that this generated code is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)\n\t// Verify that runtime/protoimpl is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)\n)\n\ntype FlowType int32\n\nconst (\n\tFlowType_FlowTypeUndefined FlowType = 0\n\t// FlowTypeCode asks users a code sent on a side channel\n\tFlowType_FlowTypeCode FlowType = 1\n\t// FlowTypeAuth currently unimplemented\n\tFlowType_FlowTypeAuth FlowType = 2\n\t// FlowTypeProof currently unimplemented\n\tFlowType_FlowTypeProof FlowType = 3\n)\n\n// Enum value maps for FlowType.\nvar (\n\tFlowType_name = map[int32]string{\n\t\t0: \"FlowTypeUndefined\",\n\t\t1: \"FlowTypeCode\",\n\t\t2: \"FlowTypeAuth\",\n\t\t3: \"FlowTypeProof\",\n\t}\n\tFlowType_value = map[string]int32{\n\t\t\"FlowTypeUndefined\": 0,\n\t\t\"FlowTypeCode\":      1,\n\t\t\"FlowTypeAuth\":      2,\n\t\t\"FlowTypeProof\":     3,\n\t}\n)\n\nfunc (x FlowType) Enum() *FlowType {\n\tp := new(FlowType)\n\t*p = x\n\treturn p\n}\n\nfunc (x FlowType) String() string {\n\treturn protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))\n}\n\nfunc (FlowType) Descriptor() protoreflect.EnumDescriptor {\n\treturn file_verifiablecredstypes_bertyverifiablecreds_proto_enumTypes[0].Descriptor()\n}\n\nfunc (FlowType) Type() protoreflect.EnumType {\n\treturn &file_verifiablecredstypes_bertyverifiablecreds_proto_enumTypes[0]\n}\n\nfunc (x FlowType) Number() protoreflect.EnumNumber {\n\treturn protoreflect.EnumNumber(x)\n}\n\n// Deprecated: Use FlowType.Descriptor instead.\nfunc (FlowType) EnumDescriptor() ([]byte, []int) {\n\treturn file_verifiablecredstypes_bertyverifiablecreds_proto_rawDescGZIP(), []int{0}\n}\n\ntype CodeStrategy int32\n\nconst (\n\tCodeStrategy_CodeStrategyUndefined CodeStrategy = 0\n\t// CodeStrategy6Digits currently unimplemented\n\tCodeStrategy_CodeStrategy6Digits CodeStrategy = 1\n\t// CodeStrategy10Chars currently unimplemented\n\tCodeStrategy_CodeStrategy10Chars CodeStrategy = 2\n\t// CodeStrategyMocked6Zeroes must only be used in testing\n\tCodeStrategy_CodeStrategyMocked6Zeroes CodeStrategy = 999\n)\n\n// Enum value maps for CodeStrategy.\nvar (\n\tCodeStrategy_name = map[int32]string{\n\t\t0:   \"CodeStrategyUndefined\",\n\t\t1:   \"CodeStrategy6Digits\",\n\t\t2:   \"CodeStrategy10Chars\",\n\t\t999: \"CodeStrategyMocked6Zeroes\",\n\t}\n\tCodeStrategy_value = map[string]int32{\n\t\t\"CodeStrategyUndefined\":     0,\n\t\t\"CodeStrategy6Digits\":       1,\n\t\t\"CodeStrategy10Chars\":       2,\n\t\t\"CodeStrategyMocked6Zeroes\": 999,\n\t}\n)\n\nfunc (x CodeStrategy) Enum() *CodeStrategy {\n\tp := new(CodeStrategy)\n\t*p = x\n\treturn p\n}\n\nfunc (x CodeStrategy) String() string {\n\treturn protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))\n}\n\nfunc (CodeStrategy) Descriptor() protoreflect.EnumDescriptor {\n\treturn file_verifiablecredstypes_bertyverifiablecreds_proto_enumTypes[1].Descriptor()\n}\n\nfunc (CodeStrategy) Type() protoreflect.EnumType {\n\treturn &file_verifiablecredstypes_bertyverifiablecreds_proto_enumTypes[1]\n}\n\nfunc (x CodeStrategy) Number() protoreflect.EnumNumber {\n\treturn protoreflect.EnumNumber(x)\n}\n\n// Deprecated: Use CodeStrategy.Descriptor instead.\nfunc (CodeStrategy) EnumDescriptor() ([]byte, []int) {\n\treturn file_verifiablecredstypes_bertyverifiablecreds_proto_rawDescGZIP(), []int{1}\n}\n\n// StateChallenge serialized and signed state used when requesting a challenge\ntype StateChallenge struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tTimestamp   []byte `protobuf:\"bytes,1,opt,name=timestamp,proto3\" json:\"timestamp,omitempty\"`\n\tNonce       []byte `protobuf:\"bytes,2,opt,name=nonce,proto3\" json:\"nonce,omitempty\"`\n\tBertyLink   string `protobuf:\"bytes,3,opt,name=berty_link,json=bertyLink,proto3\" json:\"berty_link,omitempty\"`\n\tRedirectUri string `protobuf:\"bytes,4,opt,name=redirect_uri,json=redirectUri,proto3\" json:\"redirect_uri,omitempty\"`\n\tState       string `protobuf:\"bytes,5,opt,name=state,proto3\" json:\"state,omitempty\"`\n}\n\nfunc (x *StateChallenge) Reset() {\n\t*x = StateChallenge{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_verifiablecredstypes_bertyverifiablecreds_proto_msgTypes[0]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *StateChallenge) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*StateChallenge) ProtoMessage() {}\n\nfunc (x *StateChallenge) ProtoReflect() protoreflect.Message {\n\tmi := &file_verifiablecredstypes_bertyverifiablecreds_proto_msgTypes[0]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use StateChallenge.ProtoReflect.Descriptor instead.\nfunc (*StateChallenge) Descriptor() ([]byte, []int) {\n\treturn file_verifiablecredstypes_bertyverifiablecreds_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *StateChallenge) GetTimestamp() []byte {\n\tif x != nil {\n\t\treturn x.Timestamp\n\t}\n\treturn nil\n}\n\nfunc (x *StateChallenge) GetNonce() []byte {\n\tif x != nil {\n\t\treturn x.Nonce\n\t}\n\treturn nil\n}\n\nfunc (x *StateChallenge) GetBertyLink() string {\n\tif x != nil {\n\t\treturn x.BertyLink\n\t}\n\treturn \"\"\n}\n\nfunc (x *StateChallenge) GetRedirectUri() string {\n\tif x != nil {\n\t\treturn x.RedirectUri\n\t}\n\treturn \"\"\n}\n\nfunc (x *StateChallenge) GetState() string {\n\tif x != nil {\n\t\treturn x.State\n\t}\n\treturn \"\"\n}\n\n// StateCode serialized and signed state used when requesting a code\ntype StateCode struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tTimestamp    []byte       `protobuf:\"bytes,1,opt,name=timestamp,proto3\" json:\"timestamp,omitempty\"`\n\tBertyLink    string       `protobuf:\"bytes,2,opt,name=berty_link,json=bertyLink,proto3\" json:\"berty_link,omitempty\"`\n\tCodeStrategy CodeStrategy `protobuf:\"varint,3,opt,name=code_strategy,json=codeStrategy,proto3,enum=weshnet.account.v1.CodeStrategy\" json:\"code_strategy,omitempty\"`\n\tIdentifier   string       `protobuf:\"bytes,4,opt,name=identifier,proto3\" json:\"identifier,omitempty\"`\n\tCode         string       `protobuf:\"bytes,5,opt,name=code,proto3\" json:\"code,omitempty\"`\n\tRedirectUri  string       `protobuf:\"bytes,6,opt,name=redirect_uri,json=redirectUri,proto3\" json:\"redirect_uri,omitempty\"`\n\tState        string       `protobuf:\"bytes,7,opt,name=state,proto3\" json:\"state,omitempty\"`\n}\n\nfunc (x *StateCode) Reset() {\n\t*x = StateCode{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_verifiablecredstypes_bertyverifiablecreds_proto_msgTypes[1]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *StateCode) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*StateCode) ProtoMessage() {}\n\nfunc (x *StateCode) ProtoReflect() protoreflect.Message {\n\tmi := &file_verifiablecredstypes_bertyverifiablecreds_proto_msgTypes[1]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use StateCode.ProtoReflect.Descriptor instead.\nfunc (*StateCode) Descriptor() ([]byte, []int) {\n\treturn file_verifiablecredstypes_bertyverifiablecreds_proto_rawDescGZIP(), []int{1}\n}\n\nfunc (x *StateCode) GetTimestamp() []byte {\n\tif x != nil {\n\t\treturn x.Timestamp\n\t}\n\treturn nil\n}\n\nfunc (x *StateCode) GetBertyLink() string {\n\tif x != nil {\n\t\treturn x.BertyLink\n\t}\n\treturn \"\"\n}\n\nfunc (x *StateCode) GetCodeStrategy() CodeStrategy {\n\tif x != nil {\n\t\treturn x.CodeStrategy\n\t}\n\treturn CodeStrategy_CodeStrategyUndefined\n}\n\nfunc (x *StateCode) GetIdentifier() string {\n\tif x != nil {\n\t\treturn x.Identifier\n\t}\n\treturn \"\"\n}\n\nfunc (x *StateCode) GetCode() string {\n\tif x != nil {\n\t\treturn x.Code\n\t}\n\treturn \"\"\n}\n\nfunc (x *StateCode) GetRedirectUri() string {\n\tif x != nil {\n\t\treturn x.RedirectUri\n\t}\n\treturn \"\"\n}\n\nfunc (x *StateCode) GetState() string {\n\tif x != nil {\n\t\treturn x.State\n\t}\n\treturn \"\"\n}\n\ntype AccountCryptoChallenge struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tChallenge string `protobuf:\"bytes,1,opt,name=challenge,proto3\" json:\"challenge,omitempty\"`\n}\n\nfunc (x *AccountCryptoChallenge) Reset() {\n\t*x = AccountCryptoChallenge{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_verifiablecredstypes_bertyverifiablecreds_proto_msgTypes[2]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *AccountCryptoChallenge) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*AccountCryptoChallenge) ProtoMessage() {}\n\nfunc (x *AccountCryptoChallenge) ProtoReflect() protoreflect.Message {\n\tmi := &file_verifiablecredstypes_bertyverifiablecreds_proto_msgTypes[2]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use AccountCryptoChallenge.ProtoReflect.Descriptor instead.\nfunc (*AccountCryptoChallenge) Descriptor() ([]byte, []int) {\n\treturn file_verifiablecredstypes_bertyverifiablecreds_proto_rawDescGZIP(), []int{2}\n}\n\nfunc (x *AccountCryptoChallenge) GetChallenge() string {\n\tif x != nil {\n\t\treturn x.Challenge\n\t}\n\treturn \"\"\n}\n\nvar File_verifiablecredstypes_bertyverifiablecreds_proto protoreflect.FileDescriptor\n\nvar file_verifiablecredstypes_bertyverifiablecreds_proto_rawDesc = []byte{\n\t0x0a, 0x2f, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x63, 0x72, 0x65, 0x64,\n\t0x73, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x62, 0x65, 0x72, 0x74, 0x79, 0x76, 0x65, 0x72, 0x69,\n\t0x66, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x63, 0x72, 0x65, 0x64, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74,\n\t0x6f, 0x12, 0x12, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75,\n\t0x6e, 0x74, 0x2e, 0x76, 0x31, 0x22, 0x9c, 0x01, 0x0a, 0x0e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x43,\n\t0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65,\n\t0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x74, 0x69, 0x6d,\n\t0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18,\n\t0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x12, 0x1d, 0x0a, 0x0a,\n\t0x62, 0x65, 0x72, 0x74, 0x79, 0x5f, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,\n\t0x52, 0x09, 0x62, 0x65, 0x72, 0x74, 0x79, 0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x21, 0x0a, 0x0c, 0x72,\n\t0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x04, 0x20, 0x01, 0x28,\n\t0x09, 0x52, 0x0b, 0x72, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x55, 0x72, 0x69, 0x12, 0x14,\n\t0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73,\n\t0x74, 0x61, 0x74, 0x65, 0x22, 0xfc, 0x01, 0x0a, 0x09, 0x53, 0x74, 0x61, 0x74, 0x65, 0x43, 0x6f,\n\t0x64, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18,\n\t0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,\n\t0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x65, 0x72, 0x74, 0x79, 0x5f, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x02,\n\t0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x62, 0x65, 0x72, 0x74, 0x79, 0x4c, 0x69, 0x6e, 0x6b, 0x12,\n\t0x45, 0x0a, 0x0d, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79,\n\t0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74,\n\t0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x64, 0x65,\n\t0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0c, 0x63, 0x6f, 0x64, 0x65, 0x53, 0x74,\n\t0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69,\n\t0x66, 0x69, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x64, 0x65, 0x6e,\n\t0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x05,\n\t0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x65,\n\t0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09,\n\t0x52, 0x0b, 0x72, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x55, 0x72, 0x69, 0x12, 0x14, 0x0a,\n\t0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74,\n\t0x61, 0x74, 0x65, 0x22, 0x36, 0x0a, 0x16, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x72,\n\t0x79, 0x70, 0x74, 0x6f, 0x43, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x12, 0x1c, 0x0a,\n\t0x09, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,\n\t0x52, 0x09, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x2a, 0x58, 0x0a, 0x08, 0x46,\n\t0x6c, 0x6f, 0x77, 0x54, 0x79, 0x70, 0x65, 0x12, 0x15, 0x0a, 0x11, 0x46, 0x6c, 0x6f, 0x77, 0x54,\n\t0x79, 0x70, 0x65, 0x55, 0x6e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x10, 0x00, 0x12, 0x10,\n\t0x0a, 0x0c, 0x46, 0x6c, 0x6f, 0x77, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x10, 0x01,\n\t0x12, 0x10, 0x0a, 0x0c, 0x46, 0x6c, 0x6f, 0x77, 0x54, 0x79, 0x70, 0x65, 0x41, 0x75, 0x74, 0x68,\n\t0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x46, 0x6c, 0x6f, 0x77, 0x54, 0x79, 0x70, 0x65, 0x50, 0x72,\n\t0x6f, 0x6f, 0x66, 0x10, 0x03, 0x2a, 0x7b, 0x0a, 0x0c, 0x43, 0x6f, 0x64, 0x65, 0x53, 0x74, 0x72,\n\t0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x19, 0x0a, 0x15, 0x43, 0x6f, 0x64, 0x65, 0x53, 0x74, 0x72,\n\t0x61, 0x74, 0x65, 0x67, 0x79, 0x55, 0x6e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x10, 0x00,\n\t0x12, 0x17, 0x0a, 0x13, 0x43, 0x6f, 0x64, 0x65, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79,\n\t0x36, 0x44, 0x69, 0x67, 0x69, 0x74, 0x73, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x43, 0x6f, 0x64,\n\t0x65, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x31, 0x30, 0x43, 0x68, 0x61, 0x72, 0x73,\n\t0x10, 0x02, 0x12, 0x1e, 0x0a, 0x19, 0x43, 0x6f, 0x64, 0x65, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65,\n\t0x67, 0x79, 0x4d, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x36, 0x5a, 0x65, 0x72, 0x6f, 0x65, 0x73, 0x10,\n\t0xe7, 0x07, 0x42, 0x30, 0x5a, 0x2e, 0x62, 0x65, 0x72, 0x74, 0x79, 0x2e, 0x74, 0x65, 0x63, 0x68,\n\t0x2f, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2f, 0x76, 0x32, 0x2f, 0x70, 0x6b, 0x67, 0x2f,\n\t0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x63, 0x72, 0x65, 0x64, 0x73, 0x74,\n\t0x79, 0x70, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,\n}\n\nvar (\n\tfile_verifiablecredstypes_bertyverifiablecreds_proto_rawDescOnce sync.Once\n\tfile_verifiablecredstypes_bertyverifiablecreds_proto_rawDescData = file_verifiablecredstypes_bertyverifiablecreds_proto_rawDesc\n)\n\nfunc file_verifiablecredstypes_bertyverifiablecreds_proto_rawDescGZIP() []byte {\n\tfile_verifiablecredstypes_bertyverifiablecreds_proto_rawDescOnce.Do(func() {\n\t\tfile_verifiablecredstypes_bertyverifiablecreds_proto_rawDescData = protoimpl.X.CompressGZIP(file_verifiablecredstypes_bertyverifiablecreds_proto_rawDescData)\n\t})\n\treturn file_verifiablecredstypes_bertyverifiablecreds_proto_rawDescData\n}\n\nvar file_verifiablecredstypes_bertyverifiablecreds_proto_enumTypes = make([]protoimpl.EnumInfo, 2)\nvar file_verifiablecredstypes_bertyverifiablecreds_proto_msgTypes = make([]protoimpl.MessageInfo, 3)\nvar file_verifiablecredstypes_bertyverifiablecreds_proto_goTypes = []any{\n\t(FlowType)(0),                  // 0: weshnet.account.v1.FlowType\n\t(CodeStrategy)(0),              // 1: weshnet.account.v1.CodeStrategy\n\t(*StateChallenge)(nil),         // 2: weshnet.account.v1.StateChallenge\n\t(*StateCode)(nil),              // 3: weshnet.account.v1.StateCode\n\t(*AccountCryptoChallenge)(nil), // 4: weshnet.account.v1.AccountCryptoChallenge\n}\nvar file_verifiablecredstypes_bertyverifiablecreds_proto_depIdxs = []int32{\n\t1, // 0: weshnet.account.v1.StateCode.code_strategy:type_name -> weshnet.account.v1.CodeStrategy\n\t1, // [1:1] is the sub-list for method output_type\n\t1, // [1:1] is the sub-list for method input_type\n\t1, // [1:1] is the sub-list for extension type_name\n\t1, // [1:1] is the sub-list for extension extendee\n\t0, // [0:1] is the sub-list for field type_name\n}\n\nfunc init() { file_verifiablecredstypes_bertyverifiablecreds_proto_init() }\nfunc file_verifiablecredstypes_bertyverifiablecreds_proto_init() {\n\tif File_verifiablecredstypes_bertyverifiablecreds_proto != nil {\n\t\treturn\n\t}\n\tif !protoimpl.UnsafeEnabled {\n\t\tfile_verifiablecredstypes_bertyverifiablecreds_proto_msgTypes[0].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*StateChallenge); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_verifiablecredstypes_bertyverifiablecreds_proto_msgTypes[1].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*StateCode); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_verifiablecredstypes_bertyverifiablecreds_proto_msgTypes[2].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*AccountCryptoChallenge); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n\ttype x struct{}\n\tout := protoimpl.TypeBuilder{\n\t\tFile: protoimpl.DescBuilder{\n\t\t\tGoPackagePath: reflect.TypeOf(x{}).PkgPath(),\n\t\t\tRawDescriptor: file_verifiablecredstypes_bertyverifiablecreds_proto_rawDesc,\n\t\t\tNumEnums:      2,\n\t\t\tNumMessages:   3,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   0,\n\t\t},\n\t\tGoTypes:           file_verifiablecredstypes_bertyverifiablecreds_proto_goTypes,\n\t\tDependencyIndexes: file_verifiablecredstypes_bertyverifiablecreds_proto_depIdxs,\n\t\tEnumInfos:         file_verifiablecredstypes_bertyverifiablecreds_proto_enumTypes,\n\t\tMessageInfos:      file_verifiablecredstypes_bertyverifiablecreds_proto_msgTypes,\n\t}.Build()\n\tFile_verifiablecredstypes_bertyverifiablecreds_proto = out.File\n\tfile_verifiablecredstypes_bertyverifiablecreds_proto_rawDesc = nil\n\tfile_verifiablecredstypes_bertyverifiablecreds_proto_goTypes = nil\n\tfile_verifiablecredstypes_bertyverifiablecreds_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "scenario_test.go",
    "content": "package weshnet_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n\n\tmocknet \"github.com/libp2p/go-libp2p/p2p/net/mock\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/goleak\"\n\t\"go.uber.org/zap\"\n\t\"google.golang.org/protobuf/proto\"\n\n\tweshnet \"berty.tech/weshnet/v2\"\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n\t\"berty.tech/weshnet/v2/pkg/testutil\"\n)\n\ntype testCase struct {\n\tName           string\n\tNumberOfClient int\n\tConnectFunc    weshnet.ConnectTestingProtocolFunc\n\tSpeed          testutil.Speed\n\tStability      testutil.Stability\n\tTimeout        time.Duration\n}\n\ntype testFunc func(context.Context, *testing.T, ...*weshnet.TestingProtocol)\n\n// Tests\n\nfunc TestScenario_CreateMultiMemberGroup(t *testing.T) {\n\tcases := []testCase{\n\t\t{\"2 clients/connectAll\", 2, weshnet.ConnectAll, testutil.Fast, testutil.Flappy, time.Second * 10},\n\t\t{\"3 clients/connectAll\", 3, weshnet.ConnectAll, testutil.Fast, testutil.Flappy, time.Second * 10},\n\t\t{\"3 clients/connectInLine\", 3, weshnet.ConnectInLine, testutil.Fast, testutil.Flappy, time.Second * 10},\n\t\t{\"5 clients/connectAll\", 5, weshnet.ConnectAll, testutil.Slow, testutil.Flappy, time.Second * 20},\n\t\t{\"5 clients/connectInLine\", 5, weshnet.ConnectInLine, testutil.Slow, testutil.Flappy, time.Second * 20},\n\t\t{\"8 clients/connectAll\", 8, weshnet.ConnectAll, testutil.Slow, testutil.Flappy, time.Second * 60},\n\t\t{\"8 clients/connectInLine\", 8, weshnet.ConnectInLine, testutil.Slow, testutil.Flappy, time.Second * 60},\n\t\t{\"10 clients/connectAll\", 10, weshnet.ConnectAll, testutil.Slow, testutil.Flappy, time.Second * 90},\n\t\t{\"10 clients/connectInLine\", 10, weshnet.ConnectInLine, testutil.Slow, testutil.Flappy, time.Second * 90},\n\t}\n\n\ttestingScenario(t, cases, func(ctx context.Context, t *testing.T, tps ...*weshnet.TestingProtocol) {\n\t\tcreateMultiMemberGroup(ctx, t, tps...)\n\t})\n}\n\nfunc TestScenario_MessageMultiMemberGroup(t *testing.T) {\n\tcases := []testCase{\n\t\t{\"2 clients/connectAll\", 2, weshnet.ConnectAll, testutil.Fast, testutil.Flappy, time.Second * 10},\n\t\t{\"3 clients/connectAll\", 3, weshnet.ConnectAll, testutil.Fast, testutil.Flappy, time.Second * 10},\n\t\t{\"3 clients/connectInLine\", 3, weshnet.ConnectInLine, testutil.Fast, testutil.Flappy, time.Second * 10},\n\t\t{\"5 clients/connectAll\", 5, weshnet.ConnectAll, testutil.Slow, testutil.Flappy, time.Second * 20},\n\t\t{\"5 clients/connectInLine\", 5, weshnet.ConnectInLine, testutil.Slow, testutil.Flappy, time.Second * 20},\n\t\t{\"8 clients/connectAll\", 8, weshnet.ConnectAll, testutil.Slow, testutil.Flappy, time.Second * 40},\n\t\t{\"8 clients/connectInLine\", 8, weshnet.ConnectInLine, testutil.Slow, testutil.Flappy, time.Second * 40},\n\t\t{\"10 clients/connectAll\", 10, weshnet.ConnectAll, testutil.Slow, testutil.Flappy, time.Second * 90},\n\t\t{\"10 clients/connectInLine\", 10, weshnet.ConnectInLine, testutil.Slow, testutil.Flappy, time.Second * 90},\n\t}\n\n\ttestingScenario(t, cases, func(ctx context.Context, t *testing.T, tps ...*weshnet.TestingProtocol) {\n\t\t// Create MultiMember Group\n\t\tgroupID := createMultiMemberGroup(ctx, t, tps...)\n\n\t\t// Each member sends 3 messages on MultiMember Group\n\t\tmessages := []string{\"test1\", \"test2\", \"test3\"}\n\t\tsendMessageOnGroup(ctx, t, tps, tps, groupID, messages)\n\t})\n}\n\nfunc TestScenario_GroupDeviceStatusOnMultiMemberGroup(t *testing.T) {\n\tcases := []testCase{\n\t\t{\"2 clients/connectAll\", 2, weshnet.ConnectAll, testutil.Fast, testutil.Flappy, time.Second * 10},\n\t\t{\"3 clients/connectAll\", 3, weshnet.ConnectAll, testutil.Fast, testutil.Flappy, time.Second * 10},\n\t\t{\"5 clients/connectAll\", 5, weshnet.ConnectAll, testutil.Slow, testutil.Flappy, time.Second * 20},\n\t\t{\"8 clients/connectAll\", 8, weshnet.ConnectAll, testutil.Slow, testutil.Flappy, time.Second * 60},\n\t\t{\"10 clients/connectAll\", 10, weshnet.ConnectAll, testutil.Slow, testutil.Flappy, time.Second * 90},\n\t}\n\n\ttestingScenario(t, cases, func(ctx context.Context, t *testing.T, tps ...*weshnet.TestingProtocol) {\n\t\t// Create MultiMember Group\n\t\tgroupID := createMultiMemberGroup(ctx, t, tps...)\n\n\t\ttestGroupDeviceStatus(ctx, t, groupID, tps...)\n\t})\n}\n\nfunc testGroupDeviceStatus(ctx context.Context, t *testing.T, groupID []byte, tps ...*weshnet.TestingProtocol) {\n\tntps := len(tps)\n\n\t// Get group device status\n\t{\n\t\ttestutil.LogTree(t, \"Get Group Device Status\", 1, true)\n\t\tstart := time.Now()\n\n\t\twg := sync.WaitGroup{}\n\t\tstatusReceivedLock := sync.Mutex{}\n\t\tstatusReceived := make([]map[string]struct{}, ntps)\n\t\twg.Add(ntps)\n\n\t\tnSuccess := int64(0)\n\t\tfor i := range tps {\n\t\t\tgo func(i int) {\n\t\t\t\ttp := tps[i]\n\t\t\t\tdefer wg.Done()\n\n\t\t\t\tstatusReceived[i] = map[string]struct{}{}\n\n\t\t\t\tctx, cancel := context.WithCancel(ctx)\n\t\t\t\tdefer cancel()\n\n\t\t\t\tsub, inErr := tp.Client.GroupDeviceStatus(ctx, &protocoltypes.GroupDeviceStatus_Request{\n\t\t\t\t\tGroupPk: groupID,\n\t\t\t\t})\n\t\t\t\tif inErr != nil {\n\t\t\t\t\tassert.NoError(t, inErr, fmt.Sprintf(\"error for client %d\", i))\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tfor {\n\t\t\t\t\tevt, inErr := sub.Recv()\n\t\t\t\t\tif inErr != nil {\n\t\t\t\t\t\tif inErr != io.EOF {\n\t\t\t\t\t\t\tassert.NoError(t, inErr, fmt.Sprintf(\"error for client %d\", i))\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\n\t\t\t\t\tassert.Equal(t, evt.Type, protocoltypes.GroupDeviceStatus_TypePeerConnected)\n\t\t\t\t\tconnected := &protocoltypes.GroupDeviceStatus_Reply_PeerConnected{}\n\t\t\t\t\terr := proto.Unmarshal(evt.Event, connected)\n\t\t\t\t\tassert.NoError(t, err, fmt.Sprintf(\"Unmarshal error for client %d\", i))\n\n\t\t\t\t\tstatusReceivedLock.Lock()\n\t\t\t\t\tstatusReceived[i][connected.PeerId] = struct{}{}\n\t\t\t\t\tdone := len(statusReceived[i]) == ntps-1\n\t\t\t\t\tstatusReceivedLock.Unlock()\n\n\t\t\t\t\tif done {\n\t\t\t\t\t\tn := atomic.AddInt64(&nSuccess, 1)\n\n\t\t\t\t\t\tgot := fmt.Sprintf(\"%d/%d\", n, ntps)\n\t\t\t\t\t\ttps[i].Opts.Logger.Debug(\"received all group device status\", zap.String(\"ok\", got))\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}(i)\n\t\t}\n\n\t\twg.Wait()\n\n\t\tstatusReceivedLock.Lock()\n\t\tok := true\n\t\tfor i := range statusReceived {\n\t\t\tif !assert.Equal(t, ntps-1, len(statusReceived[i]), fmt.Sprintf(\"mismatch for client %d\", i)) {\n\t\t\t\tok = false\n\t\t\t}\n\t\t}\n\t\trequire.True(t, ok)\n\t\tstatusReceivedLock.Unlock()\n\n\t\ttestutil.LogTree(t, \"duration: %s\", 1, false, time.Since(start))\n\t}\n}\n\n//\n//func TestScenario_MessageMultiMemberGroup2(t *testing.T) {\n//\tcases := []testCase{\n//\t\t{\"2 clients/connectAll\", 2, ConnectAll, testutil.Fast, testutil.Stable, time.Second * 60},\n//\t}\n//\n//\ttestingScenario(t, cases, func(ctx context.Context, t *testing.T, tps ...*TestingProtocol) {\n//\t\t// Create MultiMember Group\n//\t\tgroupID := createMultiMemberGroup(ctx, t, tps...)\n//\n//\t\tconst messageCount = 100\n//\t\t// Each member sends 3 messages on MultiMember Group\n//\t\tmessages := make([]string, messageCount)\n//\t\tfor i := 0; i < messageCount; i++ {\n//\t\t\tmessages[i] = fmt.Sprintf(\"test%d\", i)\n//\t\t}\n//\n//\t\tsendMessageOnGroup(ctx, t, tps, tps, groupID, messages)\n//\t})\n//}\n\nfunc TestScenario_MessageSeveralMultiMemberGroups(t *testing.T) {\n\tconst ngroup = 3\n\n\tcases := []testCase{\n\t\t{\"2 clients/connectAll\", 2, weshnet.ConnectAll, testutil.Fast, testutil.Flappy, time.Second * 20},\n\t\t{\"3 clients/connectAll\", 3, weshnet.ConnectAll, testutil.Fast, testutil.Flappy, time.Second * 20},\n\t\t{\"3 clients/connectInLine\", 3, weshnet.ConnectInLine, testutil.Fast, testutil.Flappy, time.Second * 20},\n\t\t{\"5 clients/connectAll\", 5, weshnet.ConnectAll, testutil.Slow, testutil.Flappy, time.Second * 60},\n\t\t{\"5 clients/connectInLine\", 5, weshnet.ConnectInLine, testutil.Slow, testutil.Flappy, time.Second * 60},\n\t\t{\"8 clients/connectAll\", 8, weshnet.ConnectAll, testutil.Slow, testutil.Flappy, time.Second * 180},\n\t\t{\"8 clients/connectInLine\", 8, weshnet.ConnectInLine, testutil.Slow, testutil.Flappy, time.Second * 180},\n\t\t{\"10 clients/connectAll\", 10, weshnet.ConnectAll, testutil.Slow, testutil.Flappy, time.Second * 300},\n\t\t{\"10 clients/connectInLine\", 10, weshnet.ConnectInLine, testutil.Slow, testutil.Flappy, time.Second * 300},\n\t}\n\n\ttestingScenario(t, cases, func(ctx context.Context, t *testing.T, tps ...*weshnet.TestingProtocol) {\n\t\tfor i := 0; i < ngroup; i++ {\n\t\t\tt.Logf(\"===== MultiMember Group #%d =====\", i+1)\n\t\t\t// Create MultiMember Group\n\t\t\tgroupID := createMultiMemberGroup(ctx, t, tps...)\n\n\t\t\t// Each member sends 3 messages on MultiMember Group\n\t\t\tmessages := []string{\"test1\", \"test2\", \"test3\"}\n\t\t\tsendMessageOnGroup(ctx, t, tps, tps, groupID, messages)\n\t\t}\n\t})\n}\n\nfunc TestScenario_AddContact(t *testing.T) {\n\tcases := []testCase{\n\t\t{\"2 clients/connectAll\", 2, weshnet.ConnectAll, testutil.Fast, testutil.Flappy, time.Second * 20},\n\t\t{\"3 clients/connectAll\", 3, weshnet.ConnectAll, testutil.Fast, testutil.Flappy, time.Second * 20},\n\t\t{\"5 clients/connectAll\", 5, weshnet.ConnectAll, testutil.Slow, testutil.Flappy, time.Second * 30},\n\t\t{\"8 clients/connectAll\", 8, weshnet.ConnectAll, testutil.Slow, testutil.Flappy, time.Second * 40},\n\t\t{\"10 clients/connectAll\", 10, weshnet.ConnectAll, testutil.Slow, testutil.Flappy, time.Second * 60},\n\t}\n\n\ttestingScenario(t, cases, func(ctx context.Context, t *testing.T, tps ...*weshnet.TestingProtocol) {\n\t\taddAsContact(ctx, t, tps, tps)\n\t})\n}\n\nfunc TestScenario_MessageContactGroup(t *testing.T) {\n\tcases := []testCase{\n\t\t{\"2 clients/connectAll\", 2, weshnet.ConnectAll, testutil.Fast, testutil.Flappy, time.Second * 20},\n\t\t{\"3 clients/connectAll\", 3, weshnet.ConnectAll, testutil.Fast, testutil.Flappy, time.Second * 20},\n\t\t{\"5 clients/connectAll\", 5, weshnet.ConnectAll, testutil.Slow, testutil.Flappy, time.Second * 30},\n\t\t{\"8 clients/connectAll\", 8, weshnet.ConnectAll, testutil.Slow, testutil.Broken, time.Second * 40},\n\t\t{\"10 clients/connectAll\", 10, weshnet.ConnectAll, testutil.Slow, testutil.Broken, time.Second * 60},\n\t}\n\n\ttestingScenario(t, cases, func(ctx context.Context, t *testing.T, tps ...*weshnet.TestingProtocol) {\n\t\t// Add accounts as contacts\n\t\taddAsContact(ctx, t, tps, tps)\n\n\t\t// Send messages between all accounts on contact groups\n\t\tmessages := []string{\"test1\", \"test2\", \"test3\"}\n\t\tsendMessageToContact(ctx, t, messages, tps)\n\t})\n}\n\nfunc TestScenario_MessageAccountGroup(t *testing.T) {\n\tcases := []testCase{\n\t\t{\"1 client/connectAll\", 1, weshnet.ConnectAll, testutil.Fast, testutil.Stable, time.Second * 10},\n\t}\n\n\ttestingScenario(t, cases, func(ctx context.Context, t *testing.T, tps ...*weshnet.TestingProtocol) {\n\t\t// Get account config\n\t\tconfig, err := tps[0].Client.ServiceGetConfiguration(ctx, &protocoltypes.ServiceGetConfiguration_Request{})\n\t\trequire.NoError(t, err)\n\t\trequire.NotNil(t, config)\n\n\t\t// Send messages on account group\n\t\tmessages := []string{\"test1\", \"test2\", \"test3\"}\n\t\tsendMessageOnGroup(ctx, t, tps, tps, config.AccountGroupPk, messages)\n\t})\n}\n\nfunc TestScenario_MessageAccountGroup_NonMocked(t *testing.T) {\n\tcases := []testCase{\n\t\t{\"1 client/connectAll\", 1, weshnet.ConnectAll, testutil.Fast, testutil.Stable, time.Second * 10},\n\t}\n\n\ttestingScenarioNonMocked(t, cases, func(ctx context.Context, t *testing.T, tps ...*weshnet.TestingProtocol) {\n\t\t// Get account config\n\t\tconfig, err := tps[0].Client.ServiceGetConfiguration(ctx, &protocoltypes.ServiceGetConfiguration_Request{})\n\t\trequire.NoError(t, err)\n\t\trequire.NotNil(t, config)\n\n\t\t// Send messages on account group\n\t\tmessages := []string{\"test1\", \"test2\", \"test3\"}\n\t\tsendMessageOnGroup(ctx, t, tps, tps, config.AccountGroupPk, messages)\n\t})\n}\n\nfunc TestScenario_MessageAccountAndMultiMemberGroups(t *testing.T) {\n\tcases := []testCase{\n\t\t{\"2 clients/connectAll\", 2, weshnet.ConnectAll, testutil.Fast, testutil.Broken, time.Second * 10},\n\t\t{\"3 clients/connectAll\", 3, weshnet.ConnectAll, testutil.Fast, testutil.Broken, time.Second * 10},\n\t\t{\"3 clients/connectInLine\", 3, weshnet.ConnectInLine, testutil.Fast, testutil.Broken, time.Second * 10},\n\t\t{\"5 clients/connectAll\", 5, weshnet.ConnectAll, testutil.Slow, testutil.Broken, time.Second * 20},\n\t\t{\"5 clients/connectInLine\", 5, weshnet.ConnectInLine, testutil.Slow, testutil.Broken, time.Second * 20},\n\t\t{\"8 clients/connectAll\", 8, weshnet.ConnectAll, testutil.Slow, testutil.Broken, time.Second * 30},\n\t\t{\"8 clients/connectInLine\", 8, weshnet.ConnectInLine, testutil.Slow, testutil.Broken, time.Second * 30},\n\t\t{\"10 clients/connectAll\", 10, weshnet.ConnectAll, testutil.Slow, testutil.Broken, time.Second * 40},\n\t\t{\"10 clients/connectInLine\", 10, weshnet.ConnectInLine, testutil.Slow, testutil.Broken, time.Second * 40},\n\t}\n\n\ttestingScenario(t, cases, func(ctx context.Context, t *testing.T, tps ...*weshnet.TestingProtocol) {\n\t\tt.Log(\"===== Send Messages on MultiMember Group =====\")\n\t\t// Create MultiMember Group\n\t\tmmGroup := createMultiMemberGroup(ctx, t, tps...)\n\n\t\t// Each member sends 3 messages on MultiMember Group\n\t\tmessages := []string{\"test1\", \"test2\", \"test3\"}\n\t\tsendMessageOnGroup(ctx, t, tps, tps, mmGroup, messages)\n\n\t\tt.Log(\"===== Send Messages on Account Group =====\")\n\t\t// Send messages on account groups\n\t\tfor _, account := range tps {\n\t\t\t// Get account config\n\t\t\tconfig, err := account.Client.ServiceGetConfiguration(ctx, &protocoltypes.ServiceGetConfiguration_Request{})\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.NotNil(t, config)\n\n\t\t\t// Send messages on account group\n\t\t\tmessages = []string{\"account1\", \"account2\", \"account3\"}\n\t\t\tsendMessageOnGroup(ctx, t, []*weshnet.TestingProtocol{account}, []*weshnet.TestingProtocol{account}, config.AccountGroupPk, messages)\n\t\t}\n\n\t\tt.Log(\"===== Send Messages again on MultiMember Group =====\")\n\t\t// Each member sends 3 messages on MultiMember Group\n\t\tmessages = []string{\"test4\", \"test5\", \"test6\"}\n\t\tsendMessageOnGroup(ctx, t, tps, tps, mmGroup, messages)\n\t})\n}\n\nfunc TestScenario_MessageAccountAndContactGroups(t *testing.T) {\n\tcases := []testCase{\n\t\t{\"2 clients/connectAll\", 2, weshnet.ConnectAll, testutil.Fast, testutil.Broken, time.Second * 10},\n\t\t{\"3 clients/connectAll\", 3, weshnet.ConnectAll, testutil.Fast, testutil.Broken, time.Second * 10},\n\t\t{\"5 clients/connectAll\", 5, weshnet.ConnectAll, testutil.Slow, testutil.Broken, time.Second * 20},\n\t\t{\"8 clients/connectAll\", 8, weshnet.ConnectAll, testutil.Slow, testutil.Broken, time.Second * 30},\n\t\t{\"10 clients/connectAll\", 10, weshnet.ConnectAll, testutil.Slow, testutil.Broken, time.Second * 40},\n\t}\n\n\ttestingScenario(t, cases, func(ctx context.Context, t *testing.T, tps ...*weshnet.TestingProtocol) {\n\t\tt.Log(\"===== Send Messages on Contact Group =====\")\n\t\t// Add accounts as contacts\n\t\taddAsContact(ctx, t, tps, tps)\n\t\t// Send messages between all accounts on contact groups\n\t\tmessages := []string{\"contact1\", \"contact2\", \"contact3\"}\n\t\tsendMessageToContact(ctx, t, messages, tps)\n\n\t\tt.Log(\"===== Send Messages on Account Group =====\")\n\t\t// Send messages on account groups\n\t\tfor _, account := range tps {\n\t\t\t// Get account config\n\t\t\tconfig, err := account.Client.ServiceGetConfiguration(ctx, &protocoltypes.ServiceGetConfiguration_Request{})\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.NotNil(t, config)\n\n\t\t\t// Send messages on account group\n\t\t\tmessages = []string{\"account1\", \"account2\", \"account3\"}\n\t\t\tsendMessageOnGroup(ctx, t, []*weshnet.TestingProtocol{account}, []*weshnet.TestingProtocol{account}, config.AccountGroupPk, messages)\n\t\t}\n\n\t\tt.Log(\"===== Send Messages again on Contact Group =====\")\n\t\t// Send messages between all accounts on contact groups\n\t\tmessages = []string{\"contact4\", \"contact5\", \"contact6\"}\n\t\tsendMessageToContact(ctx, t, messages, tps)\n\t})\n}\n\n// Helpers\n\nfunc testingScenario(t *testing.T, tcs []testCase, tf testFunc) {\n\tif os.Getenv(\"WITH_GOLEAK\") == \"1\" {\n\t\tdefer goleak.VerifyNone(t,\n\t\t\tgoleak.IgnoreTopFunction(\"github.com/syndtr/goleveldb/leveldb.(*DB).mpoolDrain\"),     // inherited from one of the imports (init)\n\t\t\tgoleak.IgnoreTopFunction(\"github.com/ipfs/go-log/writer.(*MirrorWriter).logRoutine\"), // inherited from one of the imports (init)\n\t\t\tgoleak.IgnoreTopFunction(\"github.com/jbenet/goprocess/periodic.callOnTicker.func1\"),  // inherited from github.com/ipfs/kubo/core.NewNode\n\t\t\tgoleak.IgnoreTopFunction(\"go.opencensus.io/stats/view.(*worker).start\"),              // inherited from github.com/ipfs/kubo/core.NewNode)\n\t\t\tgoleak.IgnoreTopFunction(\"github.com/desertbit/timer.timerRoutine\"),                  // inherited from github.com/ipfs/kubo/core.NewNode)\n\t\t\tgoleak.IgnoreTopFunction(\"go.opentelemetry.io/otel/instrumentation/grpctrace.wrapClientStream.func1\"),\n\t\t\tgoleak.IgnoreTopFunction(\"go.opentelemetry.io/otel/instrumentation/grpctrace.StreamClientInterceptor.func1.1\"),\n\t\t)\n\t}\n\n\tfor _, tc := range tcs {\n\t\tt.Run(tc.Name, func(t *testing.T) {\n\t\t\ttestutil.FilterStabilityAndSpeed(t, tc.Stability, tc.Speed)\n\n\t\t\tctx, cancel := context.WithCancel(context.Background())\n\t\t\tdefer cancel()\n\n\t\t\tlogger, cleanup := testutil.Logger(t)\n\t\t\tdefer cleanup()\n\n\t\t\tmn := mocknet.New()\n\t\t\tdefer mn.Close()\n\n\t\t\topts := weshnet.TestingOpts{\n\t\t\t\tMocknet:     mn,\n\t\t\t\tLogger:      logger,\n\t\t\t\tConnectFunc: tc.ConnectFunc,\n\t\t\t}\n\n\t\t\ttps, cleanup := weshnet.NewTestingProtocolWithMockedPeers(ctx, t, &opts, nil, tc.NumberOfClient)\n\t\t\tdefer cleanup()\n\n\t\t\tvar cctx context.Context\n\n\t\t\tif tc.Timeout > 0 {\n\t\t\t\tcctx, cancel = context.WithTimeout(ctx, tc.Timeout)\n\t\t\t} else {\n\t\t\t\tcctx, cancel = context.WithCancel(ctx)\n\t\t\t}\n\n\t\t\ttf(cctx, t, tps...)\n\t\t\tcancel()\n\t\t})\n\t}\n}\n\nfunc testingScenarioNonMocked(t *testing.T, tcs []testCase, tf testFunc) {\n\tif os.Getenv(\"WITH_GOLEAK\") == \"1\" {\n\t\tdefer goleak.VerifyNone(t,\n\t\t\tgoleak.IgnoreTopFunction(\"github.com/syndtr/goleveldb/leveldb.(*DB).mpoolDrain\"),     // inherited from one of the imports (init)\n\t\t\tgoleak.IgnoreTopFunction(\"github.com/ipfs/go-log/writer.(*MirrorWriter).logRoutine\"), // inherited from one of the imports (init)\n\t\t\tgoleak.IgnoreTopFunction(\"github.com/jbenet/goprocess/periodic.callOnTicker.func1\"),  // inherited from github.com/ipfs/kubo/core.NewNode\n\t\t\tgoleak.IgnoreTopFunction(\"go.opencensus.io/stats/view.(*worker).start\"),              // inherited from github.com/ipfs/kubo/core.NewNode)\n\t\t\tgoleak.IgnoreTopFunction(\"github.com/desertbit/timer.timerRoutine\"),                  // inherited from github.com/ipfs/kubo/core.NewNode)\n\t\t\tgoleak.IgnoreTopFunction(\"go.opentelemetry.io/otel/instrumentation/grpctrace.wrapClientStream.func1\"),\n\t\t\tgoleak.IgnoreTopFunction(\"go.opentelemetry.io/otel/instrumentation/grpctrace.StreamClientInterceptor.func1.1\"),\n\t\t)\n\t}\n\n\tfor _, tc := range tcs {\n\t\tt.Run(tc.Name, func(t *testing.T) {\n\t\t\ttestutil.FilterStabilityAndSpeed(t, tc.Stability, tc.Speed)\n\n\t\t\tctx, cancel := context.WithCancel(context.Background())\n\t\t\tdefer cancel()\n\n\t\t\tlogger, cleanup := testutil.Logger(t)\n\t\t\tdefer cleanup()\n\n\t\t\tmn := mocknet.New()\n\t\t\tdefer mn.Close()\n\n\t\t\topts := weshnet.TestingOpts{\n\t\t\t\tMocknet:     mn,\n\t\t\t\tLogger:      logger,\n\t\t\t\tConnectFunc: tc.ConnectFunc,\n\t\t\t}\n\n\t\t\ttps, cleanup := weshnet.NewTestingProtocolWithMockedPeers(ctx, t, &opts, nil, tc.NumberOfClient)\n\t\t\tdefer cleanup()\n\n\t\t\tvar cctx context.Context\n\n\t\t\tif tc.Timeout > 0 {\n\t\t\t\tcctx, cancel = context.WithTimeout(ctx, tc.Timeout)\n\t\t\t} else {\n\t\t\t\tcctx, cancel = context.WithCancel(ctx)\n\t\t\t}\n\n\t\t\ttf(cctx, t, tps...)\n\t\t\tcancel()\n\t\t})\n\t}\n}\n\nfunc createMultiMemberGroup(ctx context.Context, t *testing.T, tps ...*weshnet.TestingProtocol) (groupID []byte) {\n\treturn weshnet.CreateMultiMemberGroupInstance(ctx, t, tps...).PublicKey\n}\n\nfunc addAsContact(ctx context.Context, t *testing.T, senders, receivers []*weshnet.TestingProtocol) {\n\ttestutil.LogTree(t, \"Add Senders/Receivers as Contact\", 0, true)\n\tstart := time.Now()\n\tvar sendDuration, receiveDuration, acceptDuration, activateDuration time.Duration\n\n\tfor i, sender := range senders {\n\t\tfor _, receiver := range receivers {\n\t\t\tsubstart := time.Now()\n\n\t\t\t// Get sender/receiver configs\n\t\t\tsenderCfg, err := sender.Client.ServiceGetConfiguration(ctx, &protocoltypes.ServiceGetConfiguration_Request{})\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.NotNil(t, senderCfg)\n\t\t\treceiverCfg, err := receiver.Client.ServiceGetConfiguration(ctx, &protocoltypes.ServiceGetConfiguration_Request{})\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.NotNil(t, receiverCfg)\n\n\t\t\t// Setup receiver's shareable contact\n\t\t\tvar receiverRDVSeed []byte\n\n\t\t\tcrf, err := receiver.Client.ContactRequestReference(ctx, &protocoltypes.ContactRequestReference_Request{})\n\t\t\tif err != nil || !crf.Enabled || len(crf.PublicRendezvousSeed) == 0 {\n\t\t\t\t_, err = receiver.Client.ContactRequestEnable(ctx, &protocoltypes.ContactRequestEnable_Request{})\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\treceiverRDV, err := receiver.Client.ContactRequestResetReference(ctx, &protocoltypes.ContactRequestResetReference_Request{})\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.NotNil(t, receiverRDV)\n\t\t\t\treceiverRDVSeed = receiverRDV.PublicRendezvousSeed\n\t\t\t} else {\n\t\t\t\treceiverRDVSeed = crf.PublicRendezvousSeed\n\t\t\t}\n\n\t\t\treceiverSharableContact := &protocoltypes.ShareableContact{\n\t\t\t\tPk:                   receiverCfg.AccountPk,\n\t\t\t\tPublicRendezvousSeed: receiverRDVSeed,\n\t\t\t}\n\n\t\t\t// Sender sends contact request\n\t\t\t_, err = sender.Client.ContactRequestSend(ctx, &protocoltypes.ContactRequestSend_Request{\n\t\t\t\tContact: receiverSharableContact,\n\t\t\t})\n\n\t\t\t// Check if sender and receiver are the same account, should return the right error and skip\n\t\t\tif bytes.Equal(senderCfg.AccountPk, receiverCfg.AccountPk) {\n\t\t\t\trequire.Equal(t, errcode.LastCode(err), errcode.ErrCode_ErrContactRequestSameAccount)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// Check if contact request was already sent, should return right error and skip\n\t\t\treceiverWasSender := false\n\t\t\tfor j := 0; j < i; j++ {\n\t\t\t\tif senders[j] == receiver {\n\t\t\t\t\treceiverWasSender = true\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tsenderWasReceiver := false\n\t\t\tif receiverWasSender {\n\t\t\t\tfor _, r := range receivers {\n\t\t\t\t\tif r == sender {\n\t\t\t\t\t\tsenderWasReceiver = true\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif receiverWasSender && senderWasReceiver {\n\t\t\t\trequire.Equal(t, errcode.LastCode(err), errcode.ErrCode_ErrContactRequestContactAlreadyAdded)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// No other error should occur\n\t\t\trequire.NoError(t, err)\n\n\t\t\tsendDuration += time.Since(substart)\n\t\t\tsubstart = time.Now()\n\n\t\t\t// Receiver subscribes to handle incoming contact request\n\t\t\tsubCtx, subCancel := context.WithCancel(ctx)\n\t\t\tsubReceiver, err := receiver.Client.GroupMetadataList(subCtx, &protocoltypes.GroupMetadataList_Request{\n\t\t\t\tGroupPk: receiverCfg.AccountGroupPk,\n\t\t\t})\n\t\t\trequire.NoError(t, err)\n\t\t\tfound := false\n\n\t\t\t// Receiver waits for valid contact request coming from sender\n\t\t\tfor {\n\t\t\t\tevt, err := subReceiver.Recv()\n\t\t\t\tif err == io.EOF || subReceiver.Context().Err() != nil {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tif evt == nil || evt.Metadata.EventType != protocoltypes.EventType_EventTypeAccountContactRequestIncomingReceived {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\treq := &protocoltypes.AccountContactRequestIncomingReceived{}\n\t\t\t\terr = proto.Unmarshal(evt.Event, req)\n\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tif bytes.Equal(senderCfg.AccountPk, req.ContactPk) {\n\t\t\t\t\tfound = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tsubCancel()\n\t\t\trequire.True(t, found)\n\n\t\t\treceiveDuration += time.Since(substart)\n\t\t\tsubstart = time.Now()\n\n\t\t\t// Receiver accepts contact request\n\t\t\t_, err = receiver.Client.ContactRequestAccept(ctx, &protocoltypes.ContactRequestAccept_Request{\n\t\t\t\tContactPk: senderCfg.AccountPk,\n\t\t\t})\n\n\t\t\trequire.NoError(t, err)\n\n\t\t\tacceptDuration += time.Since(substart)\n\t\t\tsubstart = time.Now()\n\n\t\t\t// Both receiver and sender activate the contact group\n\t\t\tgrpInfo, err := sender.Client.GroupInfo(ctx, &protocoltypes.GroupInfo_Request{\n\t\t\t\tContactPk: receiverCfg.AccountPk,\n\t\t\t})\n\t\t\trequire.NoError(t, err)\n\n\t\t\t_, err = sender.Client.ActivateGroup(ctx, &protocoltypes.ActivateGroup_Request{\n\t\t\t\tGroupPk: grpInfo.Group.PublicKey,\n\t\t\t})\n\n\t\t\trequire.NoError(t, err)\n\n\t\t\tgrpInfo2, err := receiver.Client.GroupInfo(ctx, &protocoltypes.GroupInfo_Request{\n\t\t\t\tContactPk: senderCfg.AccountPk,\n\t\t\t})\n\t\t\trequire.NoError(t, err)\n\n\t\t\trequire.Equal(t, grpInfo.Group.PublicKey, grpInfo2.Group.PublicKey)\n\n\t\t\t_, err = receiver.Client.ActivateGroup(ctx, &protocoltypes.ActivateGroup_Request{\n\t\t\t\tGroupPk: grpInfo2.Group.PublicKey,\n\t\t\t})\n\n\t\t\trequire.NoError(t, err)\n\n\t\t\tactivateDuration += time.Since(substart)\n\t\t}\n\t}\n\n\ttestutil.LogTree(t, \"Send Contact Requests\", 1, true)\n\ttestutil.LogTree(t, \"duration: %s\", 1, false, sendDuration)\n\ttestutil.LogTree(t, \"Receive Contact Requests\", 1, true)\n\ttestutil.LogTree(t, \"duration: %s\", 1, false, receiveDuration)\n\ttestutil.LogTree(t, \"Accept Contact Requests\", 1, true)\n\ttestutil.LogTree(t, \"duration: %s\", 1, false, acceptDuration)\n\ttestutil.LogTree(t, \"Activate Contact Groups\", 1, true)\n\ttestutil.LogTree(t, \"duration: %s\", 1, false, activateDuration)\n\n\ttestutil.LogTree(t, \"duration: %s\", 0, false, time.Since(start))\n}\n\nfunc getContactGroup(ctx context.Context, t *testing.T, source *weshnet.TestingProtocol, contact *weshnet.TestingProtocol) *protocoltypes.GroupInfo_Reply {\n\t// Get contact group\n\tcontactGroup, err := source.Client.GroupInfo(ctx, &protocoltypes.GroupInfo_Request{\n\t\tContactPk: getAccountPubKey(t, contact),\n\t})\n\trequire.NoError(t, err)\n\trequire.NotNil(t, contactGroup)\n\treturn contactGroup\n}\n\nfunc sendMessageToContact(ctx context.Context, t *testing.T, messages []string, tps []*weshnet.TestingProtocol) {\n\tfor _, sender := range tps {\n\t\tfor _, receiver := range tps {\n\t\t\t// Don't try to send messages to itself using contact group\n\t\t\tif sender == receiver {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// Get contact group\n\t\t\tcontactGroup := getContactGroup(ctx, t, sender, receiver)\n\n\t\t\t// Send messages on contact group\n\t\t\tsendMessageOnGroup(ctx, t, []*weshnet.TestingProtocol{sender}, []*weshnet.TestingProtocol{receiver}, contactGroup.Group.PublicKey, messages)\n\t\t}\n\t}\n}\n\nfunc sendMessageOnGroup(ctx context.Context, t *testing.T, senders, receivers []*weshnet.TestingProtocol, groupPK []byte, messages []string) {\n\ttestutil.LogTree(t, \"Send, Receive and List Messages\", 0, true)\n\tstart := time.Now()\n\n\t// Setup expectedMessages map\n\texpectedMessages := map[string]struct{}{}\n\texpectedMessagesCount := len(messages) * len(senders)\n\texpectedMessagesLock := sync.Mutex{}\n\n\tfor _, message := range messages {\n\t\tfor _, sender := range senders {\n\t\t\texpectedMessage := getAccountB64PubKey(t, sender) + \" - \" + message\n\t\t\texpectedMessages[expectedMessage] = struct{}{}\n\t\t}\n\t}\n\n\t// Setup map to check expected messages reception\n\tsubReceivedMessages := map[string]map[string]bool{}\n\tsubReceivedMessagesCount := map[string]int{}\n\tlistReceivedMessages := map[string]map[string]bool{}\n\tlistReceivedMessagesCount := map[string]int{}\n\n\tfor _, receiver := range receivers {\n\t\tsubReceiverMap := map[string]bool{}\n\t\tlistReceiverMap := map[string]bool{}\n\n\t\tfor expectedMessage := range expectedMessages {\n\t\t\tsubReceiverMap[expectedMessage] = false\n\t\t\tlistReceiverMap[expectedMessage] = false\n\t\t}\n\n\t\treceiverID := getAccountB64PubKey(t, receiver)\n\t\tsubReceivedMessages[receiverID] = subReceiverMap\n\t\tlistReceivedMessages[receiverID] = listReceiverMap\n\t\tsubReceivedMessagesCount[receiverID] = 0\n\t\tlistReceivedMessagesCount[receiverID] = 0\n\t}\n\treceivedMessagesLock := sync.Mutex{}\n\n\t// Senders send all expected messages\n\t{\n\t\ttestutil.LogTree(t, \"Senders Send Messages\", 1, true)\n\t\tstart := time.Now()\n\n\t\tfor _, sender := range senders {\n\t\t\tsenderID := getAccountB64PubKey(t, sender)\n\t\t\tfor _, message := range messages {\n\t\t\t\t_, err := sender.Client.AppMessageSend(ctx, &protocoltypes.AppMessageSend_Request{\n\t\t\t\t\tGroupPk: groupPK,\n\t\t\t\t\tPayload: []byte(senderID + \" - \" + message),\n\t\t\t\t})\n\n\t\t\t\trequire.NoError(t, err)\n\t\t\t}\n\t\t}\n\n\t\ttestutil.LogTree(t, \"duration: %s\", 1, false, time.Since(start))\n\t}\n\n\t// Receivers receive all expected messages\n\t{\n\t\ttestutil.LogTree(t, \"Receivers Receive Messages (subscription)\", 1, true)\n\t\tstart := time.Now()\n\n\t\tvar wg sync.WaitGroup\n\t\twg.Add(len(receivers))\n\n\t\tfor _, receiver := range receivers {\n\t\t\t// Subscribe receivers to wait for incoming messages\n\t\t\tgo func(receiver *weshnet.TestingProtocol) {\n\t\t\t\tsubCtx, subCancel := context.WithCancel(ctx)\n\t\t\t\tdefer subCancel()\n\t\t\t\tdefer wg.Done()\n\n\t\t\t\tsub, err := receiver.Client.GroupMessageList(subCtx, &protocoltypes.GroupMessageList_Request{\n\t\t\t\t\tGroupPk: groupPK,\n\t\t\t\t})\n\t\t\t\tif !assert.NoError(t, err) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\treceiverID := getAccountB64PubKey(t, receiver)\n\n\t\t\t\tfor {\n\t\t\t\t\tif subCtx.Err() != nil {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\t// Receive message\n\t\t\t\t\tres, err := sub.Recv()\n\t\t\t\t\tif err == io.EOF {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tif !assert.NoError(t, err) {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\n\t\t\t\t\t// Check if received message was expected\n\t\t\t\t\texpectedMessagesLock.Lock()\n\t\t\t\t\t_, expected := expectedMessages[string(res.Message)]\n\t\t\t\t\texpectedMessagesLock.Unlock()\n\t\t\t\t\tif !expected {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\n\t\t\t\t\t// Check if message was already received\n\t\t\t\t\treceivedMessagesLock.Lock()\n\t\t\t\t\talreadyReceived := subReceivedMessages[receiverID][string(res.Message)]\n\t\t\t\t\tif alreadyReceived {\n\t\t\t\t\t\treceivedMessagesLock.Unlock()\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\n\t\t\t\t\t// Mark message as received\n\t\t\t\t\tsubReceivedMessages[receiverID][string(res.Message)] = true\n\t\t\t\t\tsubReceivedMessagesCount[receiverID]++\n\t\t\t\t\t// Return if all expected messages were received\n\t\t\t\t\tif subReceivedMessagesCount[receiverID] == expectedMessagesCount {\n\t\t\t\t\t\treceivedMessagesLock.Unlock()\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\treceivedMessagesLock.Unlock()\n\t\t\t\t}\n\t\t\t}(receiver)\n\t\t}\n\n\t\t// Wait that all receivers received messages\n\t\twg.Wait()\n\n\t\t// Check if everything is ok\n\t\tfor _, receiver := range receivers {\n\t\t\treceiverID := getAccountB64PubKey(t, receiver)\n\t\t\tassert.Equal(t, expectedMessagesCount, subReceivedMessagesCount[receiverID])\n\t\t}\n\n\t\ttestutil.LogTree(t, \"duration: %s\", 1, false, time.Since(start))\n\t}\n\n\t// Receivers list all expected messages\n\t{\n\t\ttestutil.LogTree(t, \"Receivers List Messages (store)\", 1, true)\n\t\tstart := time.Now()\n\n\t\tvar wg sync.WaitGroup\n\t\twg.Add(len(receivers))\n\n\t\tfor _, receiver := range receivers {\n\t\t\t// Subscribe receivers to wait for incoming messages\n\t\t\tgo func(receiver *weshnet.TestingProtocol) {\n\t\t\t\tsubCtx, subCancel := context.WithCancel(ctx)\n\t\t\t\tdefer subCancel()\n\t\t\t\tdefer wg.Done()\n\n\t\t\t\treq := protocoltypes.GroupMessageList_Request{\n\t\t\t\t\tGroupPk:  groupPK,\n\t\t\t\t\tUntilNow: true,\n\t\t\t\t}\n\n\t\t\t\tml, err := receiver.Client.GroupMessageList(subCtx, &req)\n\t\t\t\tif !assert.NoError(t, err) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\treceiverID := getAccountB64PubKey(t, receiver)\n\n\t\t\t\tfor {\n\t\t\t\t\tif subCtx.Err() != nil {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\t// Receive message\n\t\t\t\t\tres, err := ml.Recv()\n\t\t\t\t\tif err == io.EOF {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tif !assert.NoError(t, err) {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\n\t\t\t\t\t// Check if received message was expected\n\t\t\t\t\texpectedMessagesLock.Lock()\n\t\t\t\t\t_, expected := expectedMessages[string(res.Message)]\n\t\t\t\t\texpectedMessagesLock.Unlock()\n\t\t\t\t\tif !expected {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\n\t\t\t\t\t// Check if message was already received\n\t\t\t\t\treceivedMessagesLock.Lock()\n\t\t\t\t\talreadyReceived := listReceivedMessages[receiverID][string(res.Message)]\n\t\t\t\t\tif alreadyReceived {\n\t\t\t\t\t\treceivedMessagesLock.Unlock()\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\n\t\t\t\t\t// Mark message as received\n\t\t\t\t\tlistReceivedMessages[receiverID][string(res.Message)] = true\n\t\t\t\t\tlistReceivedMessagesCount[receiverID]++\n\t\t\t\t\t// Return if all expected messages were received\n\t\t\t\t\tif listReceivedMessagesCount[receiverID] == expectedMessagesCount {\n\t\t\t\t\t\treceivedMessagesLock.Unlock()\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\treceivedMessagesLock.Unlock()\n\t\t\t\t}\n\t\t\t}(receiver)\n\t\t}\n\n\t\t// Wait that all receivers listed messages\n\t\twg.Wait()\n\n\t\t// Check if everything is ok\n\t\tfor _, receiver := range receivers {\n\t\t\treceiverID := getAccountB64PubKey(t, receiver)\n\t\t\tassert.Equal(t, expectedMessagesCount, listReceivedMessagesCount[receiverID])\n\t\t}\n\n\t\ttestutil.LogTree(t, \"duration: %s\", 1, false, time.Since(start))\n\t}\n\ttestutil.LogTree(t, \"duration: %s\", 0, false, time.Since(start))\n}\n\nfunc getAccountPubKey(t *testing.T, tp *weshnet.TestingProtocol) []byte {\n\tt.Helper()\n\n\t_, accMemberDevice, err := tp.Opts.SecretStore.GetGroupForAccount()\n\trequire.NoError(t, err)\n\n\tpublicKeyBytes, err := accMemberDevice.Member().Raw()\n\trequire.NoError(t, err)\n\n\treturn publicKeyBytes\n}\n\nfunc getAccountB64PubKey(t *testing.T, tp *weshnet.TestingProtocol) string {\n\tt.Helper()\n\n\ttpPK := getAccountPubKey(t, tp)\n\n\treturn base64.StdEncoding.EncodeToString(tpPK)\n}\n"
  },
  {
    "path": "service.go",
    "content": "package weshnet\n\nimport (\n\t\"context\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\tmrand \"math/rand\"\n\t\"path/filepath\"\n\t\"sync\"\n\t\"time\"\n\t\"unsafe\"\n\n\t\"github.com/dgraph-io/badger/v2/options\"\n\tds \"github.com/ipfs/go-datastore\"\n\tds_sync \"github.com/ipfs/go-datastore/sync\"\n\tbadger \"github.com/ipfs/go-ds-badger2\"\n\tcoreiface \"github.com/ipfs/kubo/core/coreiface\"\n\tpubsub \"github.com/libp2p/go-libp2p-pubsub\"\n\t\"github.com/libp2p/go-libp2p/core/crypto\"\n\t\"github.com/libp2p/go-libp2p/core/event\"\n\t\"github.com/libp2p/go-libp2p/core/host\"\n\t\"github.com/libp2p/go-libp2p/core/network\"\n\t\"github.com/libp2p/go-libp2p/core/peerstore\"\n\tbackoff \"github.com/libp2p/go-libp2p/p2p/discovery/backoff\"\n\t\"github.com/libp2p/go-libp2p/p2p/host/eventbus\"\n\t\"github.com/pkg/errors\"\n\t\"github.com/prometheus/client_golang/prometheus\"\n\t\"go.uber.org/multierr\"\n\t\"go.uber.org/zap\"\n\t\"moul.io/srand\"\n\n\t\"berty.tech/go-orbit-db/baseorbitdb\"\n\t\"berty.tech/go-orbit-db/iface\"\n\t\"berty.tech/go-orbit-db/pubsub/directchannel\"\n\t\"berty.tech/go-orbit-db/pubsub/pubsubraw\"\n\t\"berty.tech/weshnet/v2/internal/bertyversion\"\n\t\"berty.tech/weshnet/v2/internal/datastoreutil\"\n\t\"berty.tech/weshnet/v2/pkg/bertyvcissuer\"\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n\t\"berty.tech/weshnet/v2/pkg/ipfsutil\"\n\tipfs_mobile \"berty.tech/weshnet/v2/pkg/ipfsutil/mobile\"\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n\t\"berty.tech/weshnet/v2/pkg/rendezvous\"\n\t\"berty.tech/weshnet/v2/pkg/secretstore\"\n\ttinder \"berty.tech/weshnet/v2/pkg/tinder\"\n\t\"berty.tech/weshnet/v2/pkg/tyber\"\n)\n\nvar _ Service = (*service)(nil)\n\n// Service is the main Berty Protocol interface\ntype Service interface {\n\tprotocoltypes.ProtocolServiceServer\n\n\tClose() error\n\tStatus() Status\n\tIpfsCoreAPI() coreiface.CoreAPI\n}\n\ntype service struct {\n\t// variables\n\tctx                    context.Context\n\tctxCancel              context.CancelFunc\n\tlogger                 *zap.Logger\n\tipfsCoreAPI            ipfsutil.ExtendedCoreAPI\n\todb                    *WeshOrbitDB\n\taccountGroupCtx        *GroupContext\n\topenedGroups           map[string]*GroupContext\n\tlock                   sync.RWMutex\n\tclose                  func() error\n\tstartedAt              time.Time\n\thost                   host.Host\n\tgrpcInsecure           bool\n\trefreshprocess         map[string]context.CancelFunc\n\tmuRefreshprocess       sync.RWMutex\n\tswiper                 *Swiper\n\tpeerStatusManager      *ConnectednessManager\n\taccountEventBus        event.Bus\n\tcontactRequestsManager *contactRequestsManager\n\tvcClient               *bertyvcissuer.Client\n\tsecretStore            secretstore.SecretStore\n\n\tprotocoltypes.UnimplementedProtocolServiceServer\n}\n\n// Opts contains optional configuration flags for building a new Client\ntype Opts struct {\n\tLogger             *zap.Logger\n\tIpfsCoreAPI        ipfsutil.ExtendedCoreAPI\n\tDatastoreDir       string\n\tRootDatastore      ds.Batching\n\tOrbitDB            *WeshOrbitDB\n\tTinderService      *tinder.Service\n\tHost               host.Host\n\tPubSub             *pubsub.PubSub\n\tGRPCInsecureMode   bool\n\tLocalOnly          bool\n\tclose              func() error\n\tSecretStore        secretstore.SecretStore\n\tPrometheusRegister prometheus.Registerer\n\t// P2PStaticRelays is only used if IpfsCoreAPI is nil\n\tP2PStaticRelays []string\n\t// P2PRdvpMaddrs is only used if TinderService is nil\n\tP2PRdvpMaddrs []string\n\n\t// These are used if OrbitDB is nil.\n\tGroupMetadataStoreType string\n\tGroupMessageStoreType  string\n}\n\nfunc (opts *Opts) applyPushDefaults() {\n\tif opts.Logger == nil {\n\t\topts.Logger = zap.NewNop()\n\t}\n\n\tif opts.PrometheusRegister == nil {\n\t\topts.PrometheusRegister = prometheus.DefaultRegisterer\n\t}\n}\n\nfunc (opts *Opts) applyDefaultsGetDatastore() error {\n\tif opts.RootDatastore == nil {\n\t\tif opts.DatastoreDir == \"\" || opts.DatastoreDir == InMemoryDirectory {\n\t\t\topts.RootDatastore = ds_sync.MutexWrap(ds.NewMapDatastore())\n\t\t} else {\n\t\t\tbopts := badger.DefaultOptions\n\t\t\tbopts.ValueLogLoadingMode = options.FileIO\n\n\t\t\tds, err := badger.NewDatastore(opts.DatastoreDir, &bopts)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"unable to init badger datastore: %w\", err)\n\t\t\t}\n\t\t\topts.RootDatastore = ds\n\n\t\t\toldClose := opts.close\n\t\t\topts.close = func() error {\n\t\t\t\tvar err error\n\t\t\t\tif oldClose != nil {\n\t\t\t\t\terr = oldClose()\n\t\t\t\t}\n\n\t\t\t\tif dserr := ds.Close(); dserr != nil {\n\t\t\t\t\terr = multierr.Append(err, fmt.Errorf(\"unable to close datastore: %w\", dserr))\n\t\t\t\t}\n\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (opts *Opts) applyDefaults(ctx context.Context) error {\n\tif opts.Logger == nil {\n\t\topts.Logger = zap.NewNop()\n\t}\n\n\trng := mrand.New(mrand.NewSource(srand.MustSecure())) // nolint:gosec // we need to use math/rand here, but it is seeded from crypto/rand\n\n\tif err := opts.applyDefaultsGetDatastore(); err != nil {\n\t\treturn err\n\t}\n\n\topts.applyPushDefaults()\n\n\tif opts.SecretStore == nil {\n\t\tsecretStore, err := secretstore.NewSecretStore(opts.RootDatastore, &secretstore.NewSecretStoreOptions{\n\t\t\tLogger: opts.Logger,\n\t\t})\n\t\tif err != nil {\n\t\t\treturn errcode.ErrCode_ErrInternal.Wrap(err)\n\t\t}\n\n\t\topts.SecretStore = secretStore\n\t}\n\n\tif opts.P2PRdvpMaddrs == nil {\n\t\topts.P2PRdvpMaddrs = []string{ipfsutil.DefaultP2PRdvpMaddr}\n\t}\n\n\tvar mnode *ipfs_mobile.IpfsMobile\n\tif opts.IpfsCoreAPI == nil {\n\t\tdsync := opts.RootDatastore\n\t\tif dsync == nil {\n\t\t\tdsync = ds_sync.MutexWrap(ds.NewMapDatastore())\n\t\t}\n\n\t\trepo, err := ipfsutil.CreateMockedRepo(dsync)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tmrepo := ipfs_mobile.NewRepoMobile(opts.DatastoreDir, repo)\n\t\t// NewIPFSMobile will apply defaults for P2PStaticRelays\n\t\tmnode, err = ipfsutil.NewIPFSMobile(ctx, mrepo, &ipfsutil.MobileOptions{\n\t\t\tLogger:          opts.Logger,\n\t\t\tP2PStaticRelays: opts.P2PStaticRelays,\n\t\t\tPeerStorePeers:  opts.P2PRdvpMaddrs,\n\t\t})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\topts.IpfsCoreAPI, err = ipfsutil.NewExtendedCoreAPIFromNode(mnode.IpfsNode)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\topts.Host = mnode.PeerHost()\n\n\t\toldClose := opts.close\n\t\topts.close = func() error {\n\t\t\tif oldClose != nil {\n\t\t\t\t_ = oldClose()\n\t\t\t}\n\n\t\t\treturn mnode.Close()\n\t\t}\n\t}\n\n\tif opts.Host == nil {\n\t\topts.Host = opts.IpfsCoreAPI\n\t}\n\n\t// setup default tinder service\n\tif opts.TinderService == nil {\n\t\tdrivers := []tinder.IDriver{}\n\n\t\t// setup loac disc\n\t\tlocaldisc, err := tinder.NewLocalDiscovery(opts.Logger, opts.Host, rng)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"unable to setup tinder localdiscovery: %w\", err)\n\t\t}\n\t\tdrivers = append(drivers, localdisc)\n\n\t\t// rdvp driver. Imitate berty configIPFSRouting\n\t\t// https://github.com/berty/berty/blob/5a8b9cb8524c1287ab2533a9e186ac8bde7f2b57/go/internal/initutil/ipfs.go#L684\n\t\trdvpeers, err := ipfsutil.ParseAndResolveMaddrs(ctx, opts.Logger, opts.P2PRdvpMaddrs)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"unable to resolve maddrs: %w\", err)\n\t\t}\n\t\taddrsFactory := tinder.PublicAddrsOnlyFactory\n\t\tif len(rdvpeers) > 0 {\n\t\t\tfor _, peer := range rdvpeers {\n\t\t\t\topts.Host.Peerstore().AddAddrs(peer.ID, peer.Addrs, peerstore.PermanentAddrTTL)\n\t\t\t\temitterclient := rendezvous.NewEmitterClient(&rendezvous.EmitterClientOptions{\n\t\t\t\t\tLogger: opts.Logger,\n\t\t\t\t})\n\n\t\t\t\t// mqttclient := rendezvous.NewMQTTClient(logger, baseopts)\n\t\t\t\tudisc := tinder.NewRendezvousDiscovery(opts.Logger, opts.Host, peer.ID, addrsFactory, rng, emitterclient)\n\t\t\t\tdrivers = append(drivers, udisc)\n\t\t\t}\n\t\t}\n\n\t\tif mnode != nil {\n\t\t\tdhtdisc := tinder.NewRoutingDiscoveryDriver(\"dht\", mnode.DHT)\n\t\t\tdrivers = append(drivers, dhtdisc)\n\t\t}\n\n\t\topts.TinderService, err = tinder.NewService(opts.Host, opts.Logger, drivers...)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"unable to setup tinder service: %w\", err)\n\t\t}\n\t}\n\n\tif opts.PubSub == nil {\n\t\tvar err error\n\n\t\tpopts := []pubsub.Option{\n\t\t\tpubsub.WithMessageSigning(true),\n\t\t\tpubsub.WithPeerExchange(true),\n\t\t}\n\n\t\tbackoffstrat := backoff.NewExponentialBackoff(\n\t\t\ttime.Second*10, time.Hour,\n\t\t\tbackoff.FullJitter,\n\t\t\ttime.Second, 10.0, 0, rng)\n\n\t\tcacheSize := 100\n\t\tdialTimeout := time.Second * 20\n\t\tbackoffconnector := func(host host.Host) (*backoff.BackoffConnector, error) {\n\t\t\treturn backoff.NewBackoffConnector(host, cacheSize, dialTimeout, backoffstrat)\n\t\t}\n\n\t\tadaptater := tinder.NewDiscoveryAdaptater(opts.Logger.Named(\"disc\"), opts.TinderService)\n\t\tpopts = append(popts, pubsub.WithDiscovery(adaptater, pubsub.WithDiscoverConnector(backoffconnector)))\n\n\t\t// pubsub.DiscoveryPollInterval = m.Node.Protocol.PollInterval\n\t\tps, err := pubsub.NewGossipSub(ctx, opts.Host, popts...)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"unable to init gossipsub: %w\", err)\n\t\t}\n\n\t\t// @NOTE(gfanton): we need to force cast here until our fix is push\n\t\t// upstream on the original go-libp2p-pubsub\n\t\t// see: https://github.com/gfanton/go-libp2p-pubsub/commit/8f4fd394f8dfcb3a5eb724a03f9e4e1e33194cbd\n\t\topts.PubSub = (*pubsub.PubSub)(unsafe.Pointer(ps))\n\t}\n\n\tif opts.OrbitDB == nil {\n\t\torbitDirectory := InMemoryDirectory\n\t\tif opts.DatastoreDir != InMemoryDirectory {\n\t\t\torbitDirectory = filepath.Join(opts.DatastoreDir, NamespaceOrbitDBDirectory)\n\t\t}\n\n\t\tpubsub := pubsubraw.NewPubSub(opts.PubSub, opts.Host.ID(), opts.Logger, nil)\n\t\todbOpts := &NewOrbitDBOptions{\n\t\t\tNewOrbitDBOptions: baseorbitdb.NewOrbitDBOptions{\n\t\t\t\tDirectory: &orbitDirectory,\n\t\t\t\tPubSub:    pubsub,\n\t\t\t\tLogger:    opts.Logger,\n\t\t\t},\n\t\t\tPrometheusRegister:     opts.PrometheusRegister,\n\t\t\tDatastore:              datastoreutil.NewNamespacedDatastore(opts.RootDatastore, ds.NewKey(NamespaceOrbitDBDatastore)),\n\t\t\tSecretStore:            opts.SecretStore,\n\t\t\tGroupMetadataStoreType: opts.GroupMetadataStoreType,\n\t\t\tGroupMessageStoreType:  opts.GroupMessageStoreType,\n\t\t}\n\n\t\tif opts.Host != nil {\n\t\t\todbOpts.DirectChannelFactory = directchannel.InitDirectChannelFactory(opts.Logger, opts.Host)\n\t\t}\n\n\t\todb, err := NewWeshOrbitDB(ctx, opts.IpfsCoreAPI, odbOpts)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\toldClose := opts.close\n\t\topts.close = func() error {\n\t\t\tif oldClose != nil {\n\t\t\t\t_ = oldClose()\n\t\t\t}\n\n\t\t\treturn odb.Close()\n\t\t}\n\n\t\topts.OrbitDB = odb\n\t}\n\n\treturn nil\n}\n\n// NewService initializes a new Service using the opts.\n// If opts.RootDatastore is nil and opts.DatastoreDir is \"\" or InMemoryDirectory, then set\n// opts.RootDatastore to an in-memory data store. Otherwise, if opts.RootDatastore is nil then set\n// opts.RootDatastore to a persistent data store at opts.DatastoreDir .\nfunc NewService(opts Opts) (_ Service, err error) {\n\tctx, cancel := context.WithCancel(context.Background())\n\n\tif err := opts.applyDefaults(ctx); err != nil {\n\t\tcancel()\n\t\treturn nil, errcode.ErrCode_TODO.Wrap(err)\n\t}\n\n\topts.Logger = opts.Logger.Named(\"pt\")\n\n\tctx, _, endSection := tyber.Section(tyber.ContextWithoutTraceID(ctx), opts.Logger, fmt.Sprintf(\"Initializing ProtocolService version %s\", bertyversion.Version))\n\tdefer func() { endSection(err, \"\") }()\n\n\taccountEventBus := eventbus.NewBus(\n\t\teventbus.WithMetricsTracer(eventbus.NewMetricsTracer(eventbus.WithRegisterer(opts.PrometheusRegister))))\n\n\tdbOpts := &iface.CreateDBOptions{\n\t\tEventBus:  accountEventBus,\n\t\tLocalOnly: &opts.LocalOnly,\n\t}\n\n\taccountGroupCtx, err := opts.OrbitDB.openAccountGroup(ctx, dbOpts, opts.IpfsCoreAPI)\n\tif err != nil {\n\t\tcancel()\n\t\treturn nil, errcode.ErrCode_TODO.Wrap(err)\n\t}\n\n\topts.Logger.Debug(\"Opened account group\", tyber.FormatStepLogFields(ctx, []tyber.Detail{{Name: \"AccountGroup\", Description: accountGroupCtx.group.String()}})...)\n\n\tvar contactRequestsManager *contactRequestsManager\n\tvar swiper *Swiper\n\tif opts.TinderService != nil {\n\t\tswiper = NewSwiper(opts.Logger, opts.TinderService, opts.OrbitDB.rotationInterval)\n\t\topts.Logger.Debug(\"Tinder swiper is enabled\", tyber.FormatStepLogFields(ctx, []tyber.Detail{})...)\n\n\t\tif contactRequestsManager, err = newContactRequestsManager(swiper, accountGroupCtx.metadataStore, opts.IpfsCoreAPI, opts.Logger); err != nil {\n\t\t\tcancel()\n\t\t\treturn nil, errcode.ErrCode_TODO.Wrap(err)\n\t\t}\n\t} else {\n\t\topts.Logger.Warn(\"No tinder driver provided, incoming and outgoing contact requests won't be enabled\", tyber.FormatStepLogFields(ctx, []tyber.Detail{})...)\n\t}\n\n\tif err := opts.SecretStore.PutGroup(ctx, accountGroupCtx.Group()); err != nil {\n\t\tcancel()\n\t\treturn nil, errcode.ErrCode_ErrInternal.Wrap(fmt.Errorf(\"unable to add account group to group datastore, err: %w\", err))\n\t}\n\n\ts := &service{\n\t\tctx:             ctx,\n\t\tctxCancel:       cancel,\n\t\thost:            opts.Host,\n\t\tipfsCoreAPI:     opts.IpfsCoreAPI,\n\t\tlogger:          opts.Logger,\n\t\todb:             opts.OrbitDB,\n\t\tclose:           opts.close,\n\t\taccountGroupCtx: accountGroupCtx,\n\t\tswiper:          swiper,\n\t\tstartedAt:       time.Now(),\n\t\topenedGroups: map[string]*GroupContext{\n\t\t\tstring(accountGroupCtx.Group().PublicKey): accountGroupCtx,\n\t\t},\n\t\tsecretStore:            opts.SecretStore,\n\t\tgrpcInsecure:           opts.GRPCInsecureMode,\n\t\trefreshprocess:         make(map[string]context.CancelFunc),\n\t\tpeerStatusManager:      NewConnectednessManager(),\n\t\taccountEventBus:        accountEventBus,\n\t\tcontactRequestsManager: contactRequestsManager,\n\t}\n\n\ts.startGroupDeviceMonitor()\n\n\treturn s, nil\n}\n\nfunc (s *service) IpfsCoreAPI() coreiface.CoreAPI {\n\treturn s.ipfsCoreAPI\n}\n\nfunc (s *service) Close() error {\n\tendSection := tyber.SimpleSection(tyber.ContextWithoutTraceID(s.ctx), s.logger, \"Closing ProtocolService\")\n\n\tvar err error\n\tpks := []crypto.PubKey{}\n\n\t// gather public keys\n\ts.lock.Lock()\n\n\tif s.contactRequestsManager != nil {\n\t\ts.contactRequestsManager.close()\n\t\ts.contactRequestsManager = nil\n\t}\n\n\tfor _, gc := range s.openedGroups {\n\t\tpk, subErr := gc.group.GetPubKey()\n\t\tif subErr != nil {\n\t\t\terr = multierr.Append(err, subErr)\n\t\t\tcontinue\n\t\t}\n\n\t\tpks = append(pks, pk)\n\t}\n\ts.lock.Unlock()\n\n\t// deactivate all groups\n\tfor _, pk := range pks {\n\t\tderr := s.deactivateGroup(pk)\n\t\tif derr != nil {\n\t\t\terr = multierr.Append(derr, derr)\n\t\t}\n\t}\n\n\terr = multierr.Append(err, s.odb.Close())\n\n\tif s.close != nil {\n\t\terr = multierr.Append(err, s.close())\n\t}\n\n\tendSection(err)\n\n\ts.ctxCancel()\n\n\treturn err\n}\n\nfunc (s *service) startGroupDeviceMonitor() {\n\tif s.host == nil {\n\t\treturn\n\t}\n\n\t// monitor exchange heads events\n\tsubHead, err := s.odb.EventBus().Subscribe(new(baseorbitdb.EventExchangeHeads),\n\t\teventbus.Name(\"weshnet/service/monitor-exchange-heads\"))\n\tif err != nil {\n\t\ts.logger.Error(\"startGroupDeviceMonitor\", zap.Error(errors.Wrap(err, \"unable to subscribe odb event\")))\n\t\treturn\n\t}\n\n\t// monitor peer connectednesschanged\n\tsubPeer, err := s.host.EventBus().Subscribe(new(event.EvtPeerConnectednessChanged),\n\t\teventbus.Name(\"weshnet/service/monitor-peer-connectedness\"))\n\tif err != nil {\n\t\ts.logger.Error(\"startGroupDeviceMonitor\", zap.Error(errors.Wrap(err, \"unable to subscribe odb event\")))\n\t\tsubHead.Close()\n\t\treturn\n\t}\n\n\tgo func() {\n\t\tdefer subHead.Close()\n\t\tdefer subPeer.Close()\n\n\t\tfor {\n\t\t\tvar evt any\n\n\t\t\tselect {\n\t\t\tcase evt = <-subHead.Out():\n\t\t\tcase evt = <-subPeer.Out():\n\t\t\tcase <-s.ctx.Done():\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tswitch e := evt.(type) {\n\t\t\tcase event.EvtPeerConnectednessChanged:\n\t\t\t\tswitch e.Connectedness {\n\t\t\t\tcase network.Connected:\n\t\t\t\t\ts.peerStatusManager.UpdateState(e.Peer, ConnectednessTypeConnected)\n\t\t\t\tcase network.NotConnected:\n\t\t\t\t\ts.peerStatusManager.UpdateState(e.Peer, ConnectednessTypeDisconnected)\n\t\t\t\t}\n\t\t\tcase baseorbitdb.EventExchangeHeads:\n\t\t\t\tif dpk, ok := s.odb.GetDevicePKForPeerID(e.Peer); ok {\n\t\t\t\t\tgkey := hex.EncodeToString(dpk.Group.PublicKey)\n\t\t\t\t\ts.peerStatusManager.AssociatePeer(gkey, e.Peer)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}()\n\n\t// get status of peers in the peerstore\n\tpeers := s.host.Peerstore().Peers()\n\tfor _, peer := range peers {\n\t\t// if we got some connected peer check their status\n\t\tif s.host.Network().Connectedness(peer) == network.Connected {\n\t\t\ts.peerStatusManager.UpdateState(peer, ConnectednessTypeConnected)\n\t\t}\n\n\t\t// if we already have some head exchange with this peer, associate it\n\t\tif dpk, ok := s.odb.GetDevicePKForPeerID(peer); ok {\n\t\t\tgkey := hex.EncodeToString(dpk.Group.PublicKey)\n\t\t\ts.peerStatusManager.AssociatePeer(gkey, peer)\n\t\t}\n\t}\n}\n\n// Status contains results of status checks\ntype Status struct {\n\tDB       error\n\tProtocol error\n}\n\nfunc (s *service) Status() Status {\n\treturn Status{\n\t\tProtocol: nil,\n\t}\n}\n"
  },
  {
    "path": "service_client.go",
    "content": "package weshnet\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"time\"\n\n\t\"go.uber.org/zap\"\n\t\"google.golang.org/grpc\"\n\n\t\"berty.tech/weshnet/v2/pkg/grpcutil\"\n\t\"berty.tech/weshnet/v2/pkg/ipfsutil\"\n\tipfs_mobile \"berty.tech/weshnet/v2/pkg/ipfsutil/mobile\"\n\t\"berty.tech/weshnet/v2/pkg/logutil\"\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n)\n\nconst (\n\tdefaultLoggingFiltersKey   = \":default:\"\n\tdefaultLoggingFiltersValue = \"info+:bty.* error+:*,-ipfs*,-*.tyber\"\n)\n\ntype ServiceClient interface {\n\tprotocoltypes.ProtocolServiceClient\n\n\tio.Closer\n}\n\ntype ServiceOption func(*Opts) error\n\n// WithLogger sets the given logger\nvar WithLogger = func(l *zap.Logger) ServiceOption {\n\treturn func(s *Opts) error {\n\t\ts.Logger = l\n\t\treturn nil\n\t}\n}\n\n// WithP2PStaticRelays sets the given P2P static relays\nvar WithP2PStaticRelays = func(p []string) ServiceOption {\n\treturn func(s *Opts) error {\n\t\ts.P2PStaticRelays = p\n\t\treturn nil\n\t}\n}\n\n// WithP2PRdvpMaddrs sets the given P2P rendezvous point addresses\nvar WithP2PRdvpMaddrs = func(p []string) ServiceOption {\n\treturn func(s *Opts) error {\n\t\ts.P2PRdvpMaddrs = p\n\t\treturn nil\n\t}\n}\n\n// NewServiceClient initializes a new ServiceClient using the opts.\n// If opts.RootDatastore is nil and opts.DatastoreDir is \"\" or InMemoryDirectory, then set\n// opts.RootDatastore to an in-memory data store. Otherwise, if opts.RootDatastore is nil then set\n// opts.RootDatastore to a persistent data store at opts.DatastoreDir .\nfunc NewServiceClient(opts Opts) (ServiceClient, error) {\n\tvar err error\n\n\tvar cleanupLogger func()\n\tif opts.Logger == nil {\n\t\tif opts.Logger, cleanupLogger, err = setupDefaultLogger(); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"unable to setup logger: %w\", err)\n\t\t}\n\t}\n\n\tsvc, err := NewService(opts)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\ts := grpc.NewServer()\n\n\tctx, cancel := context.WithTimeout(context.Background(), time.Second*10)\n\tdefer cancel()\n\n\tc, err := NewClientFromService(ctx, s, svc)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to create client from server: %w\", err)\n\t}\n\n\treturn &serviceClient{\n\t\tServiceClient: c,\n\t\tserver:        s,\n\t\tservice:       svc,\n\t\tcleanup:       cleanupLogger,\n\t}, nil\n}\n\n// NewInMemoryServiceClient creates a new in-memory Wesh protocol service and returns a gRPC\n// ServiceClient which uses a direct in-memory connection. When finished, you must call Close().\n// This creates a new Wesh account where the key store is in memory. (If you don't\n// export the data then it is lost when you call Close(). ) The IPFS node, cached data,\n// and configuration are also in memory.\nfunc NewInMemoryServiceClient(options ...ServiceOption) (ServiceClient, error) {\n\tvar opts Opts\n\tfor _, opt := range options {\n\t\tif err := opt(&opts); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\topts.DatastoreDir = InMemoryDirectory\n\treturn NewServiceClient(opts)\n}\n\n// NewPersistentServiceClient creates a Wesh protocol service using persistent storage files in the\n// directory given by the directory path. If the directory doesn't exist, this creates it with files\n// of a new Wesh account and peer identity. (If the directory doesn't exist, this will create it only\n// if the parent directory exists. Otherwise you must first create the parent directories.) However,\n// if the persistent storage files already exist, then this opens them to use the existing Wesh\n// account and peer identity. This returns a gRPC ServiceClient which uses a direct in-memory\n// connection. When finished, you must call Close().\nfunc NewPersistentServiceClient(path string, options ...ServiceOption) (ServiceClient, error) {\n\tvar opts Opts\n\tfor _, opt := range options {\n\t\tif err := opt(&opts); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\topts.DatastoreDir = path\n\n\trepo, err := ipfsutil.LoadRepoFromPath(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar cleanupLogger func()\n\tif opts.Logger == nil {\n\t\tif opts.Logger, cleanupLogger, err = setupDefaultLogger(); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"unable to setup logger: %w\", err)\n\t\t}\n\t}\n\n\tmrepo := ipfs_mobile.NewRepoMobile(path, repo)\n\tmnode, err := ipfsutil.NewIPFSMobile(context.TODO(), mrepo, &ipfsutil.MobileOptions{\n\t\tLogger:          opts.Logger,\n\t\tP2PStaticRelays: opts.P2PStaticRelays,\n\t\tPeerStorePeers:  opts.P2PRdvpMaddrs,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\topts.IpfsCoreAPI, err = ipfsutil.NewExtendedCoreAPIFromNode(mnode.IpfsNode)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tcl, err := NewServiceClient(opts)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &persistentServiceClient{\n\t\tServiceClient: cl,\n\t\tcleanup:       cleanupLogger,\n\t}, nil\n}\n\nconst ClientBufferSize = 4 * 1024 * 1024\n\ntype serviceClient struct {\n\tServiceClient // inehrit from client\n\n\tservice Service\n\tserver  *grpc.Server\n\tcleanup func()\n}\n\ntype persistentServiceClient struct {\n\tServiceClient\n\tcleanup func()\n}\n\nfunc (p *persistentServiceClient) Close() error {\n\terr := p.ServiceClient.Close()\n\n\tif p.cleanup != nil {\n\t\tp.cleanup()\n\t}\n\n\treturn err\n}\n\nfunc (c *serviceClient) Close() (err error) {\n\tc.server.GracefulStop()     // gracefully stop grpc server\n\t_ = c.ServiceClient.Close() // close client and discard error\n\n\terr = c.service.Close()\n\n\tif c.cleanup != nil {\n\t\tc.cleanup()\n\t}\n\n\treturn // return real service error\n}\n\ntype client struct {\n\tprotocoltypes.ProtocolServiceClient\n\n\tl  *grpcutil.BufListener\n\tcc *grpc.ClientConn\n}\n\nfunc (c *client) Close() error {\n\terr := c.cc.Close()\n\t_ = c.l.Close()\n\treturn err\n}\n\nfunc NewClientFromService(ctx context.Context, s *grpc.Server, svc Service, opts ...grpc.DialOption) (ServiceClient, error) {\n\tbl := grpcutil.NewBufListener(ClientBufferSize)\n\tcc, err := bl.NewClientConn(ctx, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tprotocoltypes.RegisterProtocolServiceServer(s, svc)\n\tgo func() {\n\t\t// we dont need to log the error\n\t\t_ = s.Serve(bl)\n\t}()\n\n\treturn &client{\n\t\tProtocolServiceClient: protocoltypes.NewProtocolServiceClient(cc),\n\t\tcc:                    cc,\n\t\tl:                     bl,\n\t}, nil\n}\n\nfunc setupDefaultLogger() (logger *zap.Logger, cleanup func(), err error) {\n\t// setup log from env\n\tif logfilter := os.Getenv(\"WESHNET_LOG_FILTER\"); logfilter != \"\" {\n\t\tif logfilter == defaultLoggingFiltersKey {\n\t\t\tlogfilter = defaultLoggingFiltersValue\n\t\t}\n\n\t\ts := logutil.NewStdStream(logfilter, \"color\", os.Stderr.Name())\n\t\treturn logutil.NewLogger(s)\n\t}\n\n\treturn zap.NewNop(), func() {}, nil\n}\n"
  },
  {
    "path": "service_group.go",
    "content": "package weshnet\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/libp2p/go-libp2p/core/crypto\"\n\t\"go.uber.org/zap\"\n\n\t\"berty.tech/go-orbit-db/iface\"\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n\t\"berty.tech/weshnet/v2/pkg/secretstore\"\n)\n\nfunc (s *service) getContactGroup(key crypto.PubKey) (*protocoltypes.Group, error) {\n\tgroup, err := s.secretStore.GetGroupForContact(key)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrOrbitDBOpen.Wrap(err)\n\t}\n\n\treturn group, nil\n}\n\nfunc (s *service) getGroupForPK(ctx context.Context, pk crypto.PubKey) (*protocoltypes.Group, error) {\n\tgroup, err := s.secretStore.FetchGroupByPublicKey(ctx, pk)\n\tif err == nil {\n\t\treturn group, nil\n\t} else if !errcode.Is(err, errcode.ErrCode_ErrMissingMapKey) {\n\t\treturn nil, errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\n\taccountGroup := s.getAccountGroup()\n\tif accountGroup == nil {\n\t\treturn nil, errcode.ErrCode_ErrGroupMissing\n\t}\n\n\tif err = reindexGroupDatastore(ctx, s.secretStore, accountGroup.metadataStore); err != nil {\n\t\treturn nil, errcode.ErrCode_TODO.Wrap(err)\n\t}\n\n\tgroup, err = s.secretStore.FetchGroupByPublicKey(ctx, pk)\n\tif err == nil {\n\t\treturn group, nil\n\t} else if errcode.Is(err, errcode.ErrCode_ErrMissingMapKey) {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"unknown group specified\"))\n\t}\n\n\treturn nil, errcode.ErrCode_ErrInternal.Wrap(err)\n}\n\nfunc (s *service) deactivateGroup(pk crypto.PubKey) error {\n\tid, err := pk.Raw()\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\tcg, err := s.GetContextGroupForID(id)\n\tif err != nil || cg == nil {\n\t\t// @FIXME(gfanton): should return an error code\n\t\treturn nil\n\t}\n\n\ts.lock.Lock()\n\tdefer s.lock.Unlock()\n\n\terr = cg.Close()\n\tif err != nil {\n\t\ts.logger.Error(\"unable to close group context\", zap.Error(err))\n\t}\n\n\tdelete(s.openedGroups, string(id))\n\n\tif cg.group.GroupType == protocoltypes.GroupType_GroupTypeAccount {\n\t\ts.accountGroupCtx = nil\n\t}\n\n\treturn nil\n}\n\nfunc (s *service) activateGroup(ctx context.Context, pk crypto.PubKey, localOnly bool) error {\n\tid, err := pk.Raw()\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\t_, err = s.GetContextGroupForID(id)\n\tif err != nil && err != errcode.ErrCode_ErrGroupUnknown {\n\t\treturn err\n\t}\n\n\tg, err := s.getGroupForPK(ctx, pk)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\n\ts.lock.Lock()\n\tdefer s.lock.Unlock()\n\n\t// @WIP(gfanton): do we need to use contactPK\n\tvar contactPK crypto.PubKey\n\tswitch g.GroupType {\n\tcase protocoltypes.GroupType_GroupTypeMultiMember:\n\t\t// nothing to get here, simply continue, open and activate the group\n\n\tcase protocoltypes.GroupType_GroupTypeContact:\n\t\tif s.accountGroupCtx == nil {\n\t\t\treturn errcode.ErrCode_ErrGroupActivate.Wrap(fmt.Errorf(\"accountGroupCtx is deactivated\"))\n\t\t}\n\n\t\tcontact := s.accountGroupCtx.metadataStore.GetContactFromGroupPK(id)\n\t\tif contact != nil {\n\t\t\tcontactPK, err = contact.GetPubKey()\n\t\t\tif err != nil {\n\t\t\t\treturn errcode.ErrCode_TODO.Wrap(err)\n\t\t\t}\n\t\t}\n\tcase protocoltypes.GroupType_GroupTypeAccount:\n\t\tlocalOnly = true\n\t\tif s.accountGroupCtx, err = s.odb.openAccountGroup(ctx, &iface.CreateDBOptions{EventBus: s.accountEventBus, LocalOnly: &localOnly}, s.ipfsCoreAPI); err != nil {\n\t\t\treturn err\n\t\t}\n\t\ts.openedGroups[string(id)] = s.accountGroupCtx\n\n\t\t// reinitialize contactRequestsManager\n\t\tif s.contactRequestsManager != nil {\n\t\t\ts.contactRequestsManager.close()\n\n\t\t\tif s.contactRequestsManager, err = newContactRequestsManager(s.swiper, s.accountGroupCtx.metadataStore, s.ipfsCoreAPI, s.logger); err != nil {\n\t\t\t\treturn errcode.ErrCode_TODO.Wrap(err)\n\t\t\t}\n\t\t}\n\t\treturn nil\n\tdefault:\n\t\treturn errcode.ErrCode_ErrInternal.Wrap(fmt.Errorf(\"unknown group type\"))\n\t}\n\n\tdbOpts := &iface.CreateDBOptions{LocalOnly: &localOnly}\n\tgc, err := s.odb.OpenGroup(ctx, g, dbOpts)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrGroupOpen.Wrap(err)\n\t}\n\n\tif err = gc.ActivateGroupContext(contactPK); err != nil {\n\t\tgc.Close()\n\t\treturn errcode.ErrCode_ErrGroupActivate.Wrap(err)\n\t}\n\n\ts.openedGroups[string(id)] = gc\n\n\tgc.TagGroupContextPeers(s.ipfsCoreAPI, 42)\n\treturn nil\n}\n\nfunc (s *service) GetContextGroupForID(id []byte) (*GroupContext, error) {\n\tif len(id) == 0 {\n\t\treturn nil, errcode.ErrCode_ErrInternal.Wrap(fmt.Errorf(\"no group id provided\"))\n\t}\n\n\ts.lock.RLock()\n\tdefer s.lock.RUnlock()\n\n\tcg, ok := s.openedGroups[string(id)]\n\n\tif ok {\n\t\treturn cg, nil\n\t}\n\n\treturn nil, errcode.ErrCode_ErrGroupUnknown\n}\n\nfunc reindexGroupDatastore(ctx context.Context, secretStore secretstore.SecretStore, m *MetadataStore) error {\n\tif secretStore == nil {\n\t\treturn errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"missing device keystore\"))\n\t}\n\n\tfor _, g := range m.ListMultiMemberGroups() {\n\t\tif err := secretStore.PutGroup(ctx, g); err != nil {\n\t\t\treturn errcode.ErrCode_ErrInternal.Wrap(err)\n\t\t}\n\t}\n\n\tfor _, contact := range m.ListContactsByStatus(\n\t\tprotocoltypes.ContactState_ContactStateToRequest,\n\t\tprotocoltypes.ContactState_ContactStateReceived,\n\t\tprotocoltypes.ContactState_ContactStateAdded,\n\t\tprotocoltypes.ContactState_ContactStateRemoved,\n\t\tprotocoltypes.ContactState_ContactStateDiscarded,\n\t\tprotocoltypes.ContactState_ContactStateBlocked,\n\t) {\n\t\tcPK, err := contact.GetPubKey()\n\t\tif err != nil {\n\t\t\treturn errcode.ErrCode_TODO.Wrap(err)\n\t\t}\n\n\t\tgroup, err := secretStore.GetGroupForContact(cPK)\n\t\tif err != nil {\n\t\t\treturn errcode.ErrCode_ErrInternal.Wrap(err)\n\t\t}\n\n\t\tif err := secretStore.PutGroup(ctx, group); err != nil {\n\t\t\treturn errcode.ErrCode_ErrInternal.Wrap(err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (s *service) getAccountGroup() *GroupContext {\n\ts.lock.Lock()\n\tdefer s.lock.Unlock()\n\treturn s.accountGroupCtx\n}\n"
  },
  {
    "path": "store_message.go",
    "content": "package weshnet\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"sync\"\n\n\t\"github.com/ipfs/go-cid\"\n\tcoreiface \"github.com/ipfs/kubo/core/coreiface\"\n\t\"github.com/libp2p/go-libp2p/core/crypto\"\n\t\"github.com/libp2p/go-libp2p/core/event\"\n\t\"github.com/libp2p/go-libp2p/p2p/host/eventbus\"\n\t\"go.uber.org/zap\"\n\t\"google.golang.org/protobuf/proto\"\n\n\tipfslog \"berty.tech/go-ipfs-log\"\n\t\"berty.tech/go-ipfs-log/identityprovider\"\n\tipliface \"berty.tech/go-ipfs-log/iface\"\n\t\"berty.tech/go-orbit-db/address\"\n\t\"berty.tech/go-orbit-db/iface\"\n\t\"berty.tech/go-orbit-db/stores\"\n\t\"berty.tech/go-orbit-db/stores/basestore\"\n\t\"berty.tech/go-orbit-db/stores/operation\"\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n\t\"berty.tech/weshnet/v2/pkg/logutil\"\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n\t\"berty.tech/weshnet/v2/pkg/secretstore\"\n\t\"berty.tech/weshnet/v2/pkg/tyber\"\n)\n\n// FIXME: replace cache by a circular buffer to avoid an attack by RAM saturation\ntype MessageStore struct {\n\tbasestore.BaseStore\n\teventBus event.Bus\n\temitters struct {\n\t\tgroupMessage      event.Emitter\n\t\tgroupCacheMessage event.Emitter\n\t}\n\n\tsecretStore               secretstore.SecretStore\n\tcurrentDevicePublicKey    crypto.PubKey\n\tcurrentDevicePublicKeyRaw []byte\n\tgroup                     *protocoltypes.Group\n\tgroupPublicKey            crypto.PubKey\n\tlogger                    *zap.Logger\n\n\tdeviceCaches   map[string]*groupCache\n\tmuDeviceCaches sync.RWMutex\n\n\tmessagesQueue *simpleMessageQueue\n\n\tctx    context.Context\n\tcancel context.CancelFunc\n}\n\nfunc (m *MessageStore) setLogger(l *zap.Logger) {\n\tif l == nil {\n\t\treturn\n\t}\n\n\tm.logger = l.With(logutil.PrivateString(\"group-id\", fmt.Sprintf(\"%.6s\", base64.StdEncoding.EncodeToString(m.group.PublicKey))))\n}\n\nfunc (m *MessageStore) openMessage(ctx context.Context, e ipfslog.Entry) (*protocoltypes.GroupMessageEvent, error) {\n\tif e == nil {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput\n\t}\n\n\top, err := operation.ParseOperation(e)\n\tif err != nil {\n\t\tm.logger.Error(\"unable to parse operation\", zap.Error(err))\n\t\treturn nil, err\n\t}\n\n\tenv, headers, err := m.secretStore.OpenEnvelopeHeaders(op.GetValue(), m.group)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrCryptoDecrypt.Wrap(err)\n\t}\n\n\tdevicePublicKey, err := crypto.UnmarshalEd25519PublicKey(headers.DevicePk)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\tif !m.secretStore.IsChainKeyKnownForDevice(ctx, m.groupPublicKey, devicePublicKey) {\n\t\tif err := m.addToMessageQueue(ctx, e); err != nil {\n\t\t\tm.logger.Error(\"unable to add message to cache\", zap.Error(err))\n\t\t}\n\n\t\treturn nil, fmt.Errorf(\"no secret for device\")\n\t}\n\n\treturn m.processMessage(ctx, &messageItem{\n\t\top:      op,\n\t\tenv:     env,\n\t\theaders: headers,\n\t\thash:    e.GetHash(),\n\t})\n}\n\ntype groupCache struct {\n\tself, hasKnownChainKey bool\n\tlocker                 sync.Locker\n\tqueue                  *priorityMessageQueue\n}\n\nfunc (m *MessageStore) CacheSizeForDevicePK(devicePK []byte) (size int, ok bool) {\n\tm.muDeviceCaches.RLock()\n\tvar device *groupCache\n\tif device, ok = m.deviceCaches[string(devicePK)]; ok {\n\t\tsize = device.queue.Size()\n\t}\n\tm.muDeviceCaches.RUnlock()\n\treturn\n}\n\nfunc (m *MessageStore) ProcessMessageQueueForDevicePK(ctx context.Context, devicePK []byte) {\n\tm.muDeviceCaches.Lock()\n\tif device, ok := m.deviceCaches[string(devicePK)]; ok {\n\t\tdevicePublicKey, errDevice := crypto.UnmarshalEd25519PublicKey(devicePK)\n\n\t\tif errDevice != nil {\n\t\t\tm.logger.Error(\"unable to process message, unmarshal of device pk failed\", logutil.PrivateBinary(\"devicepk\", devicePK))\n\t\t} else if device.hasKnownChainKey = m.secretStore.IsChainKeyKnownForDevice(ctx, m.groupPublicKey, devicePublicKey); !device.hasKnownChainKey {\n\t\t\tm.logger.Error(\"unable to process message, no secret found for device pk\", logutil.PrivateBinary(\"devicepk\", devicePK))\n\t\t} else if next := device.queue.Next(); next != nil {\n\t\t\t// let's try processing one message from the queue.\n\t\t\t// if it succeeds, the whole queue should be added for processing.\n\t\t\tm.messagesQueue.Add(next)\n\t\t}\n\t}\n\tm.muDeviceCaches.Unlock()\n}\n\nfunc (m *MessageStore) processMessage(ctx context.Context, message *messageItem) (*protocoltypes.GroupMessageEvent, error) {\n\t// process message\n\tmsg, err := m.secretStore.OpenEnvelopePayload(ctx, message.env, message.headers, m.groupPublicKey, m.currentDevicePublicKey, message.hash)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to open the envelope: %w\", err)\n\t}\n\n\terr = m.secretStore.UpdateOutOfStoreGroupReferences(ctx, message.headers.DevicePk, message.headers.Counter, m.group)\n\tif err != nil {\n\t\tm.logger.Error(\"unable to update push group references\", zap.Error(err))\n\t}\n\n\tentry := message.op.GetEntry()\n\teventContext := newEventContext(entry.GetHash(), entry.GetNext(), m.group)\n\treturn &protocoltypes.GroupMessageEvent{\n\t\tEventContext: eventContext,\n\t\tHeaders:      message.headers,\n\t\tMessage:      msg.GetPlaintext(),\n\t}, nil\n}\n\nfunc (m *MessageStore) processMessageLoop(ctx context.Context, tracer *messageMetricsTracer) {\n\tfor {\n\t\t// wait for next message\n\t\tmessage, ok := m.messagesQueue.WaitForItem(ctx)\n\t\tif !ok {\n\t\t\t// context expired, return\n\t\t\treturn\n\t\t}\n\n\t\t// get or create a device cache for the device from which we received the message.\n\t\tdevice, hasKnownChainKey := m.getOrCreateDeviceCache(ctx, message, tracer)\n\t\tif device == nil {\n\t\t\t// unknown device, lets keep moving\n\t\t\tcontinue\n\t\t} else if !hasKnownChainKey {\n\t\t\t// we dont know the chain key yet, add message to the device cache\n\t\t\tdevice.queue.Add(message)\n\t\t\t_ = m.emitters.groupCacheMessage.Emit(*message)\n\t\t\tcontinue\n\t\t}\n\n\t\t// actually process the message\n\t\tevt, err := m.processMessage(ctx, message)\n\t\tif err != nil {\n\t\t\tm.logger.Error(\"unable to process message\", zap.Error(err))\n\n\t\t\t// if we got any error here, put (back) the message into the device queue\n\t\t\t// for ex: `too many open files` error\n\t\t\tdevice.queue.Add(message)\n\t\t\t_ = m.emitters.groupCacheMessage.Emit(*message)\n\t\t\tcontinue\n\t\t}\n\n\t\t// if we get here we probably can process other messages (if any) in the device queue\n\t\tm.processDeviceMessagesInQueue(device)\n\n\t\t// emit new message event\n\t\tif err := m.emitters.groupMessage.Emit(evt); err != nil {\n\t\t\tm.logger.Warn(\"unable to emit group message event\", zap.Error(err))\n\t\t}\n\t}\n}\n\nfunc (m *MessageStore) getOrCreateDeviceCache(ctx context.Context, message *messageItem, tracer *messageMetricsTracer) (device *groupCache, hasKnownChainKey bool) {\n\tdevicePublicKeyString := string(message.headers.DevicePk)\n\n\tm.muDeviceCaches.Lock()\n\tdefer m.muDeviceCaches.Unlock()\n\n\tdevice, ok := m.deviceCaches[devicePublicKeyString]\n\tif !ok {\n\t\tdevicePublicKey, err := crypto.UnmarshalEd25519PublicKey(message.headers.DevicePk)\n\t\tif err != nil {\n\t\t\tm.logger.Error(\"unable to process message, unmarshal of device pk failed\", logutil.PrivateBinary(\"devicepk\", message.headers.DevicePk))\n\t\t\treturn nil, false\n\t\t}\n\n\t\thasSecret := m.secretStore.IsChainKeyKnownForDevice(ctx, m.groupPublicKey, devicePublicKey)\n\t\tdevice = &groupCache{\n\t\t\tself:             bytes.Equal(m.currentDevicePublicKeyRaw, message.headers.DevicePk),\n\t\t\tqueue:            newPriorityMessageQueue(\"undecrypted\", tracer),\n\t\t\tlocker:           &sync.RWMutex{},\n\t\t\thasKnownChainKey: hasSecret,\n\t\t}\n\t\tm.deviceCaches[devicePublicKeyString] = device\n\t}\n\n\treturn device, device.hasKnownChainKey\n}\n\n// process the whole device queue (if any) into to the message queue\nfunc (m *MessageStore) processDeviceMessagesInQueue(device *groupCache) {\n\t_ = device.queue.NextAll(func(next *messageItem) error {\n\t\tm.messagesQueue.Add(next)\n\t\treturn nil\n\t})\n}\n\nfunc (m *MessageStore) addToMessageQueue(_ context.Context, e ipfslog.Entry) error {\n\tif e == nil {\n\t\treturn errcode.ErrCode_ErrInvalidInput\n\t}\n\n\top, err := operation.ParseOperation(e)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tenv, headers, err := m.secretStore.OpenEnvelopeHeaders(op.GetValue(), m.group)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrCryptoDecrypt.Wrap(err)\n\t}\n\n\tmsg := &messageItem{\n\t\thash:    e.GetHash(),\n\t\tenv:     env,\n\t\theaders: headers,\n\t\top:      op,\n\t}\n\n\tm.messagesQueue.Add(msg)\n\n\treturn nil\n}\n\n// FIXME: use iterator instead to reduce resource usage (require go-ipfs-log improvements)\nfunc (m *MessageStore) ListEvents(ctx context.Context, since, until []byte, reverse bool) (<-chan *protocoltypes.GroupMessageEvent, error) {\n\tentries, err := getEntriesInRange(m.OpLog().GetEntries().Reverse().Slice(), since, until)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tout := make(chan *protocoltypes.GroupMessageEvent)\n\n\tgo func() {\n\t\titerateOverEntries(\n\t\t\tentries,\n\t\t\treverse,\n\t\t\tfunc(entry ipliface.IPFSLogEntry) {\n\t\t\t\tmessage, err := m.openMessage(ctx, entry)\n\t\t\t\tif err != nil {\n\t\t\t\t\tm.logger.Error(\"unable to open message\", zap.Error(err))\n\t\t\t\t} else {\n\t\t\t\t\tout <- message\n\t\t\t\t\tm.logger.Info(\"message store - sent 1 event from log history\")\n\t\t\t\t}\n\t\t\t},\n\t\t)\n\n\t\tclose(out)\n\t}()\n\n\treturn out, nil\n}\n\nfunc (m *MessageStore) AddMessage(ctx context.Context, payload []byte) (operation.Operation, error) {\n\tctx, newTrace := tyber.ContextWithTraceID(ctx)\n\n\tif newTrace {\n\t\tm.logger.Debug(\"Sending message to group \"+base64.RawURLEncoding.EncodeToString(m.group.PublicKey), tyber.FormatTraceLogFields(ctx)...)\n\t}\n\n\tm.logger.Debug(\n\t\tfmt.Sprintf(\"Adding message to store with payload of %d bytes\", len(payload)),\n\t\ttyber.FormatStepLogFields(\n\t\t\tctx,\n\t\t\t[]tyber.Detail{\n\t\t\t\t{Name: \"Payload\", Description: string(payload)},\n\t\t\t},\n\t\t)...,\n\t)\n\n\treturn messageStoreAddMessage(ctx, m.group, m, payload)\n}\n\nfunc messageStoreAddMessage(ctx context.Context, g *protocoltypes.Group, m *MessageStore, payload []byte) (operation.Operation, error) {\n\tmsg := &protocoltypes.EncryptedMessage{\n\t\tPlaintext:        payload,\n\t\tProtocolMetadata: &protocoltypes.ProtocolMetadata{},\n\t}\n\tmsgBytes, err := proto.Marshal(msg)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\n\tsealedEnvelope, err := m.secretStore.SealEnvelope(ctx, g, msgBytes)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrCryptoEncrypt.Wrap(err)\n\t}\n\tm.logger.Debug(\n\t\t\"Message sealed successfully in secretbox envelope\",\n\t\ttyber.FormatStepLogFields(\n\t\t\tctx,\n\t\t\t[]tyber.Detail{\n\t\t\t\t{Name: \"Cleartext size\", Description: fmt.Sprintf(\"%d bytes\", len(msgBytes))},\n\t\t\t\t{Name: \"Ciphertext size\", Description: fmt.Sprintf(\"%d bytes\", len(sealedEnvelope))},\n\t\t\t},\n\t\t)...,\n\t)\n\n\top := operation.NewOperation(nil, \"ADD\", sealedEnvelope)\n\n\te, err := m.AddOperation(ctx, op, nil)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrOrbitDBAppend.Wrap(err)\n\t}\n\tm.logger.Debug(\n\t\t\"Envelope added to orbit-DB log successfully\",\n\t\ttyber.FormatStepLogFields(ctx, []tyber.Detail{})...,\n\t)\n\n\top, err = operation.ParseOperation(e)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrOrbitDBDeserialization.Wrap(err)\n\t}\n\n\tm.logger.Debug(\n\t\t\"Operation parsed by orbit-DB successfully\",\n\t\ttyber.FormatStepLogFields(ctx, []tyber.Detail{{Name: \"CID\", Description: op.GetEntry().GetHash().String()}})...,\n\t)\n\n\treturn op, nil\n}\n\nfunc constructorFactoryGroupMessage(s *WeshOrbitDB, logger *zap.Logger) iface.StoreConstructor {\n\tmetricsTracer := newMessageMetricsTracer(s.prometheusRegister)\n\treturn func(ipfs coreiface.CoreAPI, identity *identityprovider.Identity, addr address.Address, options *iface.NewStoreOptions) (iface.Store, error) {\n\t\tg, err := s.getGroupFromOptions(options)\n\t\tif err != nil {\n\t\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(err)\n\t\t}\n\n\t\tgroupPublicKey, err := g.GetPubKey()\n\t\tif err != nil {\n\t\t\treturn nil, errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t\t}\n\n\t\tif options.EventBus == nil {\n\t\t\toptions.EventBus = s.EventBus()\n\t\t}\n\n\t\treplication := false\n\n\t\tstore := &MessageStore{\n\t\t\teventBus:       options.EventBus,\n\t\t\tsecretStore:    s.secretStore,\n\t\t\tmessagesQueue:  newMessageQueue(\"cache\", metricsTracer),\n\t\t\tgroup:          g,\n\t\t\tgroupPublicKey: groupPublicKey,\n\t\t\tlogger:         logger,\n\t\t\tdeviceCaches:   make(map[string]*groupCache),\n\t\t}\n\n\t\tif s.replicationMode {\n\t\t\treplication = true\n\t\t} else {\n\t\t\tcurrentMemberDevice, err := s.secretStore.GetOwnMemberDeviceForGroup(g)\n\n\t\t\tif err != nil {\n\t\t\t\tif errcode.Is(err, errcode.ErrCode_ErrInvalidInput) {\n\t\t\t\t\treplication = true\n\t\t\t\t} else {\n\t\t\t\t\treturn nil, errcode.ErrCode_ErrOrbitDBInit.Wrap(err)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tstore.currentDevicePublicKey = currentMemberDevice.Device()\n\t\t\t\tstore.currentDevicePublicKeyRaw, err = store.currentDevicePublicKey.Raw()\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, errcode.ErrCode_ErrOrbitDBInit.Wrap(err)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tstore.ctx, store.cancel = context.WithCancel(context.Background())\n\n\t\tgo func() {\n\t\t\tstore.processMessageLoop(store.ctx, metricsTracer)\n\t\t\tlogger.Debug(\"store message process loop ended\", zap.Error(store.ctx.Err()))\n\t\t}()\n\n\t\tif store.emitters.groupMessage, err = store.eventBus.Emitter(new(*protocoltypes.GroupMessageEvent)); err != nil {\n\t\t\tstore.cancel()\n\t\t\treturn nil, errcode.ErrCode_ErrOrbitDBInit.Wrap(err)\n\t\t}\n\n\t\t// for debug/test purpose\n\t\tif store.emitters.groupCacheMessage, err = store.eventBus.Emitter(new(messageItem)); err != nil {\n\t\t\tstore.cancel()\n\t\t\treturn nil, errcode.ErrCode_ErrOrbitDBInit.Wrap(err)\n\t\t}\n\n\t\toptions.Index = basestore.NewNoopIndex\n\n\t\tif err := store.InitBaseStore(ipfs, identity, addr, options); err != nil {\n\t\t\tstore.cancel()\n\t\t\treturn nil, errcode.ErrCode_ErrOrbitDBInit.Wrap(err)\n\t\t}\n\n\t\tif replication {\n\t\t\treturn store, nil\n\t\t}\n\n\t\tchSub, err := store.EventBus().Subscribe([]any{\n\t\t\tnew(stores.EventWrite),\n\t\t\tnew(stores.EventReplicated),\n\t\t}, eventbus.Name(\"weshnet/store-message\"), eventbus.BufSize(128))\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"unable to subscribe to store events\")\n\t\t}\n\n\t\tgo func(ctx context.Context) {\n\t\t\tdefer chSub.Close()\n\t\t\tfor {\n\t\t\t\tvar e any\n\t\t\t\tselect {\n\t\t\t\tcase e = <-chSub.Out():\n\t\t\t\tcase <-ctx.Done():\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tvar entries []ipfslog.Entry\n\n\t\t\t\tswitch evt := e.(type) {\n\t\t\t\tcase stores.EventWrite:\n\t\t\t\t\tentries = []ipfslog.Entry{evt.Entry}\n\n\t\t\t\tcase stores.EventReplicated:\n\t\t\t\t\tentries = evt.Entries\n\t\t\t\t}\n\n\t\t\t\tfor _, entry := range entries {\n\t\t\t\t\tctx = tyber.ContextWithConstantTraceID(ctx, \"msgrcvd-\"+entry.GetHash().String())\n\t\t\t\t\tstore.logger.Debug(\"Received message store event\", tyber.FormatTraceLogFields(ctx)...)\n\t\t\t\t\tstore.logger.Debug(\n\t\t\t\t\t\t\"Message store event\",\n\t\t\t\t\t\ttyber.FormatStepLogFields(ctx, []tyber.Detail{{Name: \"RawEvent\", Description: fmt.Sprint(e)}})...,\n\t\t\t\t\t)\n\n\t\t\t\t\tif err := store.addToMessageQueue(ctx, entry); err != nil {\n\t\t\t\t\t\tlogger.Error(\"unable to add message to queue\", zap.Error(err))\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}(store.ctx)\n\n\t\treturn store, nil\n\t}\n}\n\nfunc (m *MessageStore) GetMessageByCID(c cid.Cid) (operation.Operation, error) {\n\tlogEntry, ok := m.OpLog().Get(c)\n\tif !ok {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"unable to find message entry\"))\n\t}\n\n\top, err := operation.ParseOperation(logEntry)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\treturn op, nil\n}\n\nfunc (m *MessageStore) GetOutOfStoreMessageEnvelope(_ context.Context, c cid.Cid) (*protocoltypes.OutOfStoreMessageEnvelope, error) {\n\top, err := m.GetMessageByCID(c)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(err)\n\t}\n\n\tenv, headers, err := m.secretStore.OpenEnvelopeHeaders(op.GetValue(), m.group)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\tsealedMessageEnvelope, err := m.secretStore.SealOutOfStoreMessageEnvelope(c, env, headers, m.group)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\n\treturn sealedMessageEnvelope, nil\n}\n\nfunc (m *MessageStore) Close() error {\n\tm.cancel()\n\treturn m.BaseStore.Close()\n}\n"
  },
  {
    "path": "store_message_metrics.go",
    "content": "package weshnet\n\nimport (\n\t\"encoding/hex\"\n\t\"fmt\"\n\n\t\"github.com/prometheus/client_golang/prometheus\"\n\n\t\"berty.tech/weshnet/v2/internal/queue\"\n)\n\nconst messageMetricNamespace = \"bty_store_message\"\n\nvar (\n\tcollectorMessageStoreQueueLength = prometheus.NewGaugeVec(\n\t\tprometheus.GaugeOpts{\n\t\t\tNamespace: messageMetricNamespace,\n\t\t\tName:      \"message_queue_length\",\n\t\t\tHelp:      \"message queue length\",\n\t\t}, []string{\"kind\", \"device_pk\"},\n\t)\n\tcollectorsMessageStore = []prometheus.Collector{\n\t\tcollectorMessageStoreQueueLength,\n\t}\n)\n\nvar _ queue.MetricsTracer[*messageItem] = (*messageMetricsTracer)(nil)\n\ntype messageMetricsTracer struct {\n\treg prometheus.Registerer\n}\n\nfunc newMessageMetricsTracer(reg prometheus.Registerer) (mt *messageMetricsTracer) {\n\tmt = &messageMetricsTracer{reg: reg}\n\tfor _, collector := range collectorsMessageStore {\n\t\tif err := reg.Register(collector); err != nil {\n\t\t\tif _, ok := err.(prometheus.AlreadyRegisteredError); !ok {\n\t\t\t\tpanic(fmt.Errorf(\"message metrics errors: %w\", err))\n\t\t\t}\n\n\t\t\treturn\n\t\t}\n\t}\n\t// reg.MustRegister(collectorsMessageStore...)\n\treturn\n}\n\nfunc (s *messageMetricsTracer) ItemQueued(name string, m *messageItem) {\n\tcollectorMessageStoreQueueLength.WithLabelValues(\n\t\tname, hex.EncodeToString(m.headers.DevicePk),\n\t).Inc()\n}\n\nfunc (s *messageMetricsTracer) ItemPop(name string, m *messageItem) {\n\tcollectorMessageStoreQueueLength.WithLabelValues(\n\t\tname, hex.EncodeToString(m.headers.DevicePk),\n\t).Dec()\n}\n"
  },
  {
    "path": "store_message_queue.go",
    "content": "package weshnet\n\nimport (\n\t\"github.com/ipfs/go-cid\"\n\n\t\"berty.tech/go-orbit-db/stores/operation\"\n\t\"berty.tech/weshnet/v2/internal/queue\"\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n)\n\n// An Item is something we manage in a priority queue.\ntype messageItem struct {\n\top      operation.Operation\n\tenv     *protocoltypes.MessageEnvelope\n\theaders *protocoltypes.MessageHeaders\n\thash    cid.Cid\n}\n\nfunc (m *messageItem) Counter() uint64 {\n\treturn m.headers.Counter\n}\n\ntype simpleMessageQueue = queue.SimpleQueue[*messageItem]\n\nfunc newMessageQueue(name string, tracer queue.MetricsTracer[*messageItem]) *simpleMessageQueue {\n\treturn queue.NewSimpleQueue[*messageItem](name, tracer)\n}\n\ntype priorityMessageQueue = queue.PriorityQueue[*messageItem]\n\nfunc newPriorityMessageQueue(name string, tracer queue.MetricsTracer[*messageItem]) *priorityMessageQueue {\n\treturn queue.NewPriorityQueue[*messageItem](name, tracer)\n}\n"
  },
  {
    "path": "store_message_test.go",
    "content": "package weshnet\n\nimport (\n\t\"container/ring\"\n\t\"context\"\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/libp2p/go-libp2p/p2p/host/eventbus\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\tipfslog \"berty.tech/go-ipfs-log\"\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n\t\"berty.tech/weshnet/v2/pkg/testutil\"\n)\n\nfunc countEntries(out <-chan *protocoltypes.GroupMessageEvent) int {\n\tfound := 0\n\n\tfor range out {\n\t\tfound++\n\t}\n\n\treturn found\n}\n\nfunc Test_AddMessage_ListMessages_manually_supplying_secrets(t *testing.T) {\n\ttestutil.FilterSpeed(t, testutil.Slow)\n\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\tmemberCount := 2\n\tdeviceCount := 1\n\tentriesCount := 25\n\n\ttestMsg1 := []byte(\"first message\")\n\n\tpeers, _, cleanup := CreatePeersWithGroupTest(ctx, t, \"/tmp/message_test\", memberCount, deviceCount)\n\tdefer cleanup()\n\n\tdPK0 := peers[0].GC.DevicePubKey()\n\tds0For1, err := peers[0].SecretStore.GetShareableChainKey(ctx, peers[0].GC.Group(), peers[1].GC.MemberPubKey())\n\trequire.NoError(t, err)\n\trequire.NotNil(t, ds0For1)\n\n\terr = peers[1].SecretStore.RegisterChainKey(ctx, peers[0].GC.Group(), dPK0, ds0For1)\n\trequire.NoError(t, err)\n\n\t_, err = peers[0].GC.MessageStore().AddMessage(ctx, testMsg1)\n\trequire.NoError(t, err)\n\n\t<-time.After(time.Millisecond * 500)\n\n\tout, err := peers[0].GC.MessageStore().ListEvents(ctx, nil, nil, false)\n\trequire.NoError(t, err)\n\n\trequire.Equal(t, 1, countEntries(out))\n\n\twatcherCtx, watcherCancel := context.WithTimeout(ctx, time.Second*5)\n\tchSub, err := peers[1].GC.MessageStore().EventBus().Subscribe(new(*protocoltypes.GroupMessageEvent))\n\trequire.NoError(t, err)\n\tdefer chSub.Close()\n\n\tgo func() {\n\t\tfor {\n\t\t\tselect {\n\t\t\tcase <-chSub.Out():\n\t\t\tcase <-watcherCtx.Done():\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tc, err := peers[1].GC.MessageStore().ListEvents(watcherCtx, nil, nil, false)\n\t\t\tif !assert.NoError(t, err) {\n\t\t\t\twatcherCancel()\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tif countEntries(c) == entriesCount+1 {\n\t\t\t\twatcherCancel()\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}()\n\n\tfor i := 0; i < entriesCount; i++ {\n\t\tpayload := []byte(fmt.Sprintf(\"test message %d\", i))\n\t\t_, err = peers[0].GC.MessageStore().AddMessage(ctx, payload)\n\t\trequire.NoError(t, err)\n\t}\n\n\t<-watcherCtx.Done()\n\n\tout, err = peers[1].GC.MessageStore().ListEvents(ctx, nil, nil, false)\n\trequire.NoError(t, err)\n\n\t<-time.After(time.Second)\n\n\trequire.Equal(t, 1+entriesCount, countEntries(out))\n\n\t// TODO: check that ListEvents can be called multiple times with the same output\n\t// TODO: check that message are correctly ordered\n\t// TODO: check that message are correctly decrypted\n\t// TODO: check that message sender is correct\n\t// TODO: check that message parents IDs are valid\n\t// TODO: check that message IDs are valid\n}\n\nfunc bufferCount(buffer *ring.Ring) int {\n\tcount := 0\n\tbuffer.Do(func(f any) {\n\t\tif _, ok := f.(ipfslog.Entry); ok {\n\t\t\tcount++\n\t\t}\n\t})\n\treturn count\n}\n\nfunc Test_Add_Messages_To_Cache(t *testing.T) {\n\ttestutil.FilterSpeed(t, testutil.Fast)\n\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\tmemberCount := 2\n\tdeviceCount := 1\n\tentriesCount := 50\n\n\ttestMsg1 := []byte(\"last message\")\n\n\tpeers, _, cleanup := CreatePeersWithGroupTest(ctx, t, \"/tmp/message_test\", memberCount, deviceCount)\n\tdefer cleanup()\n\n\tdPK0 := peers[0].GC.DevicePubKey()\n\tdPK0Raw, err := dPK0.Raw()\n\trequire.NoError(t, err)\n\tds0For1, err := peers[0].SecretStore.GetShareableChainKey(ctx, peers[0].GC.Group(), peers[1].GC.MemberPubKey())\n\trequire.NoError(t, err)\n\trequire.NotNil(t, ds0For1)\n\n\tcevent, err := peers[0].GC.MessageStore().EventBus().Subscribe(\n\t\tnew(*protocoltypes.GroupMessageEvent), eventbus.BufSize(entriesCount))\n\trequire.NoError(t, err)\n\n\tcadded, err := peers[1].GC.MessageStore().EventBus().Subscribe(\n\t\tnew(messageItem), eventbus.BufSize(entriesCount))\n\trequire.NoError(t, err)\n\n\tfor i := 0; i < entriesCount; i++ {\n\t\tpayload := []byte(fmt.Sprintf(\"test message %d\", i))\n\t\t_, err = peers[0].GC.MessageStore().AddMessage(ctx, payload)\n\t\trequire.NoError(t, err)\n\t}\n\n\t// check that all events has been received on peer 1\n\tfor i := 0; i < entriesCount; i++ {\n\t\tselect {\n\t\tcase <-cevent.Out():\n\t\tcase <-time.After(time.Second):\n\t\t\trequire.FailNow(t, \"timeout while waiting for group message event\")\n\t\t\treturn\n\t\t}\n\t}\n\tcevent.Close()\n\n\tclist, err := peers[0].GC.MessageStore().ListEvents(ctx, nil, nil, false)\n\trequire.NoError(t, err)\n\tcount := countEntries(clist)\n\trequire.Equal(t, entriesCount, count)\n\n\t// check that messages has been replicated on peer 2\n\tfor i := 0; i < entriesCount; i++ {\n\t\tselect {\n\t\tcase <-cadded.Out():\n\t\tcase <-time.After(time.Second * 5):\n\t\t\trequire.FailNow(t, \"timeout while waiting for replicated event\")\n\t\t\treturn\n\t\t}\n\t}\n\tcadded.Close()\n\n\t// time.Sleep(time.Millisecond * 500)\n\n\tsize, ok := peers[1].GC.MessageStore().CacheSizeForDevicePK(dPK0Raw)\n\trequire.True(t, ok)\n\trequire.Equal(t, entriesCount, size)\n\n\terr = peers[1].SecretStore.RegisterChainKey(ctx, peers[0].GC.Group(), dPK0, ds0For1)\n\trequire.NoError(t, err)\n\n\tcevent, err = peers[1].GC.MessageStore().EventBus().Subscribe(\n\t\tnew(*protocoltypes.GroupMessageEvent), eventbus.BufSize(entriesCount))\n\trequire.NoError(t, err)\n\n\tpeers[1].GC.MessageStore().ProcessMessageQueueForDevicePK(ctx, dPK0Raw)\n\n\t// check that all events has been received on peer 2\n\tfor i := 0; i < entriesCount; i++ {\n\t\tselect {\n\t\tcase <-cevent.Out():\n\t\tcase <-time.After(time.Second):\n\t\t\trequire.FailNow(t, \"timeout while waiting for group message event\")\n\t\t\treturn\n\t\t}\n\t}\n\tcevent.Close()\n\n\tsize, ok = peers[1].GC.MessageStore().CacheSizeForDevicePK(dPK0Raw)\n\trequire.True(t, ok)\n\trequire.Equal(t, 0, size)\n\n\t_, err = peers[0].GC.MessageStore().AddMessage(ctx, testMsg1)\n\trequire.NoError(t, err)\n\n\tsize, ok = peers[1].GC.MessageStore().CacheSizeForDevicePK(dPK0Raw)\n\trequire.True(t, ok)\n\trequire.Equal(t, 0, size)\n}\n"
  },
  {
    "path": "store_metadata.go",
    "content": "package weshnet\n\nimport (\n\t\"context\"\n\tcrand \"crypto/rand\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"io\"\n\t\"slices\"\n\t\"strings\"\n\n\tcoreiface \"github.com/ipfs/kubo/core/coreiface\"\n\t\"github.com/libp2p/go-libp2p/core/crypto\"\n\t\"github.com/libp2p/go-libp2p/core/event\"\n\t\"github.com/libp2p/go-libp2p/p2p/host/eventbus\"\n\t\"go.uber.org/zap\"\n\t\"google.golang.org/protobuf/proto\"\n\n\tipfslog \"berty.tech/go-ipfs-log\"\n\t\"berty.tech/go-ipfs-log/identityprovider\"\n\tipliface \"berty.tech/go-ipfs-log/iface\"\n\t\"berty.tech/go-orbit-db/address\"\n\t\"berty.tech/go-orbit-db/iface\"\n\t\"berty.tech/go-orbit-db/stores\"\n\t\"berty.tech/go-orbit-db/stores/basestore\"\n\t\"berty.tech/go-orbit-db/stores/operation\"\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n\t\"berty.tech/weshnet/v2/pkg/secretstore\"\n\t\"berty.tech/weshnet/v2/pkg/tyber\"\n)\n\ntype MetadataStore struct {\n\tbasestore.BaseStore\n\teventBus event.Bus\n\temitters struct {\n\t\tgroupMetadata    event.Emitter\n\t\tmetadataReceived event.Emitter\n\t}\n\n\tgroup              *protocoltypes.Group\n\tmemberDevice       secretstore.OwnMemberDevice\n\tdevicePublicKeyRaw []byte\n\tsecretStore        secretstore.SecretStore\n\tlogger             *zap.Logger\n\n\tctx    context.Context\n\tcancel context.CancelFunc\n}\n\nfunc isMultiMemberGroup(m *MetadataStore) bool {\n\treturn m.group.GroupType == protocoltypes.GroupType_GroupTypeMultiMember\n}\n\nfunc isAccountGroup(m *MetadataStore) bool {\n\treturn m.group.GroupType == protocoltypes.GroupType_GroupTypeAccount\n}\n\nfunc isContactGroup(m *MetadataStore) bool {\n\treturn m.group.GroupType == protocoltypes.GroupType_GroupTypeContact\n}\n\nfunc (m *MetadataStore) typeChecker(types ...func(m *MetadataStore) bool) bool {\n\tfor _, t := range types {\n\t\tif t(m) {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\nfunc (m *MetadataStore) setLogger(l *zap.Logger) {\n\tif l == nil {\n\t\treturn\n\t}\n\n\t// m.logger = l.Named(\"store\").With(logutil.PrivateString(\"group-id\", fmt.Sprintf(\"%.6s\", base64.StdEncoding.EncodeToString(m.group.PublicKey))))\n\tm.logger = l.Named(\"metastore\")\n\n\tif index, ok := m.Index().(loggable); ok {\n\t\tindex.setLogger(m.logger)\n\t}\n}\n\nfunc openMetadataEntry(log ipfslog.Log, e ipfslog.Entry, g *protocoltypes.Group) (*protocoltypes.GroupMetadataEvent, proto.Message, error) {\n\top, err := operation.ParseOperation(e)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tmeta, event, err := openGroupEnvelope(g, op.GetValue())\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tmetaEvent, err := newGroupMetadataEventFromEntry(log, e, meta, event, g)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\treturn metaEvent, event, err\n}\n\n// not used\n// func (m *MetadataStore) openMetadataEntry(e ipfslog.Entry) (*protocoltypes.GroupMetadataEvent, proto.Message, error) {\n// \treturn openMetadataEntry(m.OpLog(), e, m.group, m.devKS)\n// }\n\n// FIXME: use iterator instead to reduce resource usage (require go-ipfs-log improvements)\nfunc (m *MetadataStore) ListEvents(_ context.Context, since, until []byte, reverse bool) (<-chan *protocoltypes.GroupMetadataEvent, error) {\n\tentries, err := getEntriesInRange(m.OpLog().GetEntries().Reverse().Slice(), since, until)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tout := make(chan *protocoltypes.GroupMetadataEvent)\n\n\tgo func() {\n\t\titerateOverEntries(\n\t\t\tentries,\n\t\t\treverse,\n\t\t\tfunc(entry ipliface.IPFSLogEntry) {\n\t\t\t\tevent, _, err := openMetadataEntry(m.OpLog(), entry, m.group)\n\t\t\t\tif err != nil {\n\t\t\t\t\tm.logger.Error(\"unable to open metadata event\", zap.Error(err))\n\t\t\t\t} else {\n\t\t\t\t\tout <- event\n\t\t\t\t\tm.logger.Info(\"metadata store - sent 1 event from log history\")\n\t\t\t\t}\n\t\t\t},\n\t\t)\n\n\t\tclose(out)\n\t}()\n\n\treturn out, nil\n}\n\nfunc (m *MetadataStore) AddDeviceToGroup(ctx context.Context) (operation.Operation, error) {\n\tmd, err := m.secretStore.GetOwnMemberDeviceForGroup(m.group)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\n\treturn MetadataStoreAddDeviceToGroup(ctx, m, m.group, md)\n}\n\nfunc MetadataStoreAddDeviceToGroup(ctx context.Context, m *MetadataStore, g *protocoltypes.Group, md secretstore.OwnMemberDevice) (operation.Operation, error) {\n\tdevice, err := md.Device().Raw()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\tmember, err := md.Member().Raw()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\tk, err := m.GetMemberByDevice(md.Device())\n\tif err == nil && k != nil {\n\t\treturn nil, nil\n\t}\n\n\tmemberSig, err := md.MemberSign(device)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrCryptoSignature.Wrap(err)\n\t}\n\n\tevent := &protocoltypes.GroupMemberDeviceAdded{\n\t\tMemberPk:  member,\n\t\tDevicePk:  device,\n\t\tMemberSig: memberSig,\n\t}\n\n\tsig, err := signProtoWithDevice(event, md)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrCryptoSignature.Wrap(err)\n\t}\n\n\tm.logger.Info(\"announcing device on store\")\n\n\treturn metadataStoreAddEvent(ctx, m, g, protocoltypes.EventType_EventTypeGroupMemberDeviceAdded, event, sig)\n}\n\nfunc (m *MetadataStore) SendSecret(ctx context.Context, memberPK crypto.PubKey) (operation.Operation, error) {\n\tok, err := m.Index().(*metadataStoreIndex).areSecretsAlreadySent(memberPK)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(err)\n\t}\n\n\tif ok {\n\t\treturn nil, errcode.ErrCode_ErrGroupSecretAlreadySentToMember\n\t}\n\n\tif devs, err := m.GetDevicesForMember(memberPK); len(devs) == 0 || err != nil {\n\t\tm.logger.Warn(\"sending secret to an unknown group member\")\n\t}\n\n\tencryptedSecret, err := m.secretStore.GetShareableChainKey(ctx, m.group, memberPK)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrCryptoEncrypt.Wrap(err)\n\t}\n\n\treturn MetadataStoreSendSecret(ctx, m, m.group, m.memberDevice, memberPK, encryptedSecret)\n}\n\nfunc MetadataStoreSendSecret(ctx context.Context, m *MetadataStore, g *protocoltypes.Group, md secretstore.OwnMemberDevice, memberPK crypto.PubKey, encryptedSecret []byte) (operation.Operation, error) {\n\tdevicePKRaw, err := md.Device().Raw()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\tmemberPKRaw, err := memberPK.Raw()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\tevent := &protocoltypes.GroupDeviceChainKeyAdded{\n\t\tDevicePk:     devicePKRaw,\n\t\tDestMemberPk: memberPKRaw,\n\t\tPayload:      encryptedSecret,\n\t}\n\n\tsig, err := signProtoWithDevice(event, md)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrCryptoSignature.Wrap(err)\n\t}\n\n\treturn metadataStoreAddEvent(ctx, m, g, protocoltypes.EventType_EventTypeGroupDeviceChainKeyAdded, event, sig)\n}\n\nfunc (m *MetadataStore) ClaimGroupOwnership(ctx context.Context, groupSK crypto.PrivKey) (operation.Operation, error) {\n\tif !m.typeChecker(isMultiMemberGroup) {\n\t\treturn nil, errcode.ErrCode_ErrGroupInvalidType\n\t}\n\n\tevent := &protocoltypes.MultiMemberGroupInitialMemberAnnounced{\n\t\tMemberPk: m.devicePublicKeyRaw,\n\t}\n\n\tsig, err := signProtoWithPrivateKey(event, groupSK)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrCryptoSignature.Wrap(err)\n\t}\n\n\treturn metadataStoreAddEvent(ctx, m, m.group, protocoltypes.EventType_EventTypeMultiMemberGroupInitialMemberAnnounced, event, sig)\n}\n\nfunc signProtoWithDevice(message proto.Message, memberDevice secretstore.OwnMemberDevice) ([]byte, error) {\n\tdata, err := proto.Marshal(message)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\tsig, err := memberDevice.DeviceSign(data)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrCryptoSignature.Wrap(err)\n\t}\n\n\treturn sig, nil\n}\n\nfunc signProtoWithPrivateKey(message proto.Message, sk crypto.PrivKey) ([]byte, error) {\n\tdata, err := proto.Marshal(message)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\tsig, err := sk.Sign(data)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrCryptoSignature.Wrap(err)\n\t}\n\n\treturn sig, nil\n}\n\nfunc metadataStoreAddEvent(ctx context.Context, m *MetadataStore, g *protocoltypes.Group, eventType protocoltypes.EventType, event proto.Message, sig []byte) (operation.Operation, error) {\n\tctx, newTrace := tyber.ContextWithTraceID(ctx)\n\ttyberLogError := tyber.LogError\n\tif newTrace {\n\t\tm.logger.Debug(fmt.Sprintf(\"Sending %s to %s group %s\", strings.TrimPrefix(eventType.String(), \"EventType\"), strings.TrimPrefix(g.GroupType.String(), \"GroupType\"), base64.RawURLEncoding.EncodeToString(g.PublicKey)), tyber.FormatTraceLogFields(ctx)...)\n\t\ttyberLogError = tyber.LogFatalError\n\t}\n\n\tenv, err := sealGroupEnvelope(g, eventType, event, sig)\n\tif err != nil {\n\t\treturn nil, tyberLogError(ctx, m.logger, \"Failed to seal group envelope\", errcode.ErrCode_ErrCryptoSignature.Wrap(err))\n\t}\n\tm.logger.Debug(fmt.Sprintf(\"Sealed group envelope (%d bytes)\", len(env)), tyber.FormatStepLogFields(ctx, []tyber.Detail{})...)\n\n\top := operation.NewOperation(nil, \"ADD\", env)\n\te, err := m.AddOperation(ctx, op, nil)\n\tif err != nil {\n\t\treturn nil, tyberLogError(ctx, m.logger, \"Failed to add operation on log\", errcode.ErrCode_ErrOrbitDBAppend.Wrap(err))\n\t}\n\tm.logger.Debug(\"Added operation on log\", tyber.FormatStepLogFields(ctx, []tyber.Detail{\n\t\t{Name: \"CID\", Description: e.GetHash().String()},\n\t})...)\n\n\top, err = operation.ParseOperation(e)\n\tif err != nil {\n\t\treturn nil, tyberLogError(ctx, m.logger, \"Failed to parse operation returned by log\", errcode.ErrCode_ErrOrbitDBDeserialization.Wrap(err))\n\t}\n\n\tif newTrace {\n\t\tm.logger.Debug(\"Added metadata on log successfully\", tyber.FormatStepLogFields(ctx, []tyber.Detail{}, tyber.EndTrace)...)\n\t}\n\treturn op, nil\n}\n\nfunc (m *MetadataStore) ListContacts() map[string]*AccountContact {\n\treturn m.Index().(*metadataStoreIndex).listContacts()\n}\n\nfunc (m *MetadataStore) ListVerifiedCredentials() []*protocoltypes.AccountVerifiedCredentialRegistered {\n\treturn m.Index().(*metadataStoreIndex).listVerifiedCredentials()\n}\n\nfunc (m *MetadataStore) GetMemberByDevice(pk crypto.PubKey) (crypto.PubKey, error) {\n\treturn m.Index().(*metadataStoreIndex).getMemberByDevice(pk)\n}\n\nfunc (m *MetadataStore) GetDevicesForMember(pk crypto.PubKey) ([]crypto.PubKey, error) {\n\treturn m.Index().(*metadataStoreIndex).getDevicesForMember(pk)\n}\n\nfunc (m *MetadataStore) ListAdmins() []crypto.PubKey {\n\tif m.typeChecker(isContactGroup, isAccountGroup) {\n\t\treturn m.ListMembers()\n\t}\n\n\treturn m.Index().(*metadataStoreIndex).listAdmins()\n}\n\nfunc (m *MetadataStore) GetIncomingContactRequestsStatus() (bool, *protocoltypes.ShareableContact) {\n\tif !m.typeChecker(isAccountGroup) {\n\t\treturn false, nil\n\t}\n\n\tenabled := m.Index().(*metadataStoreIndex).contactRequestsEnabled()\n\tseed := m.Index().(*metadataStoreIndex).contactRequestsSeed()\n\n\trawMemberDevice, err := m.memberDevice.Member().Raw()\n\tif err != nil {\n\t\tm.logger.Error(\"unable to serialize member public key\", zap.Error(err))\n\t\treturn enabled, nil\n\t}\n\n\tcontactRef := &protocoltypes.ShareableContact{\n\t\tPk:                   rawMemberDevice,\n\t\tPublicRendezvousSeed: seed,\n\t}\n\n\treturn enabled, contactRef\n}\n\nfunc (m *MetadataStore) ListMembers() []crypto.PubKey {\n\tif m.typeChecker(isAccountGroup, isContactGroup, isMultiMemberGroup) {\n\t\treturn m.Index().(*metadataStoreIndex).listMembers()\n\t}\n\n\treturn nil\n}\n\nfunc (m *MetadataStore) ListDevices() []crypto.PubKey {\n\treturn m.Index().(*metadataStoreIndex).listDevices()\n}\n\nfunc (m *MetadataStore) ListMultiMemberGroups() []*protocoltypes.Group {\n\tif !m.typeChecker(isAccountGroup) {\n\t\treturn nil\n\t}\n\n\tidx, ok := m.Index().(*metadataStoreIndex)\n\tif !ok {\n\t\treturn nil\n\t}\n\tidx.lock.Lock()\n\tdefer idx.lock.Unlock()\n\n\tgroups := []*protocoltypes.Group(nil)\n\n\tfor _, g := range idx.groups {\n\t\tif g.state != accountGroupJoinedStateJoined {\n\t\t\tcontinue\n\t\t}\n\n\t\tgroups = append(groups, g.group)\n\t}\n\n\treturn groups\n}\n\nfunc (m *MetadataStore) ListOtherMembersDevices() []crypto.PubKey {\n\treturn m.Index().(*metadataStoreIndex).listOtherMembersDevices()\n}\n\nfunc (m *MetadataStore) GetRequestOwnMetadataForContact(pk []byte) ([]byte, error) {\n\tidx, ok := m.Index().(*metadataStoreIndex)\n\tif !ok {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"invalid index type\"))\n\t}\n\n\tidx.lock.Lock()\n\tdefer idx.lock.Unlock()\n\n\tmeta, ok := idx.contactRequestMetadata[string(pk)]\n\tif !ok {\n\t\treturn nil, errcode.ErrCode_ErrMissingMapKey.Wrap(fmt.Errorf(\"no metadata found for specified contact\"))\n\t}\n\n\treturn meta, nil\n}\n\nfunc (m *MetadataStore) ListContactsByStatus(states ...protocoltypes.ContactState) []*protocoltypes.ShareableContact {\n\tif !m.typeChecker(isAccountGroup) {\n\t\treturn nil\n\t}\n\n\tidx, ok := m.Index().(*metadataStoreIndex)\n\tif !ok {\n\t\treturn nil\n\t}\n\tidx.lock.Lock()\n\tdefer idx.lock.Unlock()\n\n\tcontacts := []*protocoltypes.ShareableContact(nil)\n\n\tfor _, c := range idx.contacts {\n\t\thasState := slices.Contains(states, c.state)\n\n\t\tif hasState {\n\t\t\tcontacts = append(contacts, c.contact)\n\t\t}\n\t}\n\n\treturn contacts\n}\n\nfunc (m *MetadataStore) GetContactFromGroupPK(groupPK []byte) *protocoltypes.ShareableContact {\n\tif !m.typeChecker(isAccountGroup) {\n\t\treturn nil\n\t}\n\n\tidx, ok := m.Index().(*metadataStoreIndex)\n\tif !ok {\n\t\treturn nil\n\t}\n\tidx.lock.Lock()\n\tdefer idx.lock.Unlock()\n\n\tcontact, ok := idx.contactsFromGroupPK[string(groupPK)]\n\tif !ok || contact == nil {\n\t\treturn nil\n\t}\n\n\treturn contact.contact\n}\n\nfunc (m *MetadataStore) checkIfInGroup(pk []byte) bool {\n\tidx, ok := m.Index().(*metadataStoreIndex)\n\tif !ok {\n\t\treturn false\n\t}\n\n\tidx.lock.Lock()\n\tdefer idx.lock.Unlock()\n\n\tif existingGroup, ok := idx.groups[string(pk)]; ok && existingGroup.state == accountGroupJoinedStateJoined {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// GroupJoin indicates the payload includes that the deviceKeystore has joined a group\nfunc (m *MetadataStore) GroupJoin(ctx context.Context, g *protocoltypes.Group) (operation.Operation, error) {\n\tif !m.typeChecker(isAccountGroup) {\n\t\treturn nil, errcode.ErrCode_ErrGroupInvalidType\n\t}\n\n\tif err := g.IsValid(); err != nil {\n\t\treturn nil, errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\tif m.checkIfInGroup(g.PublicKey) {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"already present in group\"))\n\t}\n\n\treturn m.attributeSignAndAddEvent(ctx, &protocoltypes.AccountGroupJoined{\n\t\tGroup: g,\n\t}, protocoltypes.EventType_EventTypeAccountGroupJoined)\n}\n\n// GroupLeave indicates the payload includes that the deviceKeystore has left a group\nfunc (m *MetadataStore) GroupLeave(ctx context.Context, pk crypto.PubKey) (operation.Operation, error) {\n\tif !m.typeChecker(isAccountGroup) {\n\t\treturn nil, errcode.ErrCode_ErrGroupInvalidType\n\t}\n\n\tif pk == nil {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput\n\t}\n\n\tbytes, err := pk.Raw()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\tif !m.checkIfInGroup(bytes) {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput\n\t}\n\n\treturn m.groupAction(ctx, pk, &protocoltypes.AccountGroupLeft{}, protocoltypes.EventType_EventTypeAccountGroupLeft)\n}\n\n// ContactRequestDisable indicates the payload includes that the deviceKeystore has disabled incoming contact requests\nfunc (m *MetadataStore) ContactRequestDisable(ctx context.Context) (operation.Operation, error) {\n\tif !m.typeChecker(isAccountGroup) {\n\t\treturn nil, errcode.ErrCode_ErrGroupInvalidType\n\t}\n\n\treturn m.attributeSignAndAddEvent(ctx, &protocoltypes.AccountContactRequestDisabled{}, protocoltypes.EventType_EventTypeAccountContactRequestDisabled)\n}\n\n// ContactRequestEnable indicates the payload includes that the deviceKeystore has enabled incoming contact requests\nfunc (m *MetadataStore) ContactRequestEnable(ctx context.Context) (operation.Operation, error) {\n\tif !m.typeChecker(isAccountGroup) {\n\t\treturn nil, errcode.ErrCode_ErrGroupInvalidType\n\t}\n\n\treturn m.attributeSignAndAddEvent(ctx, &protocoltypes.AccountContactRequestEnabled{}, protocoltypes.EventType_EventTypeAccountContactRequestEnabled)\n}\n\n// ContactRequestReferenceReset indicates the payload includes that the deviceKeystore has a new contact request reference\nfunc (m *MetadataStore) ContactRequestReferenceReset(ctx context.Context) (operation.Operation, error) {\n\tif !m.typeChecker(isAccountGroup) {\n\t\treturn nil, errcode.ErrCode_ErrGroupInvalidType\n\t}\n\n\tseed, err := genNewSeed()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err)\n\t}\n\n\treturn m.attributeSignAndAddEvent(ctx, &protocoltypes.AccountContactRequestReferenceReset{\n\t\tPublicRendezvousSeed: seed,\n\t}, protocoltypes.EventType_EventTypeAccountContactRequestReferenceReset)\n}\n\n// ContactRequestOutgoingEnqueue indicates the payload includes that the deviceKeystore will attempt to send a new contact request\nfunc (m *MetadataStore) ContactRequestOutgoingEnqueue(ctx context.Context, contact *protocoltypes.ShareableContact, ownMetadata []byte) (operation.Operation, error) {\n\tctx, _ = tyber.ContextWithTraceID(ctx)\n\n\tb64GroupPK := base64.RawURLEncoding.EncodeToString(m.group.PublicKey)\n\tm.logger.Debug(\"Enqueuing contact request\", tyber.FormatStepLogFields(ctx, []tyber.Detail{{Name: \"GroupPK\", Description: fmt.Sprint(b64GroupPK)}})...)\n\n\tif !m.typeChecker(isAccountGroup) {\n\t\treturn nil, errcode.ErrCode_ErrGroupInvalidType\n\t}\n\n\tif err := contact.CheckFormat(); err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(err)\n\t}\n\n\taccountPublicKey := m.memberDevice.Member()\n\tif contact.IsSamePK(accountPublicKey) {\n\t\treturn nil, errcode.ErrCode_ErrContactRequestSameAccount\n\t}\n\n\tpk, err := contact.GetPubKey()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\tif m.checkContactStatus(pk, protocoltypes.ContactState_ContactStateAdded) {\n\t\treturn nil, errcode.ErrCode_ErrContactRequestContactAlreadyAdded\n\t}\n\n\tif m.checkContactStatus(pk, protocoltypes.ContactState_ContactStateRemoved, protocoltypes.ContactState_ContactStateDiscarded, protocoltypes.ContactState_ContactStateReceived) {\n\t\treturn m.ContactRequestOutgoingSent(ctx, pk)\n\t}\n\n\top, err := m.attributeSignAndAddEvent(ctx, &protocoltypes.AccountContactRequestOutgoingEnqueued{\n\t\tContact: &protocoltypes.ShareableContact{\n\t\t\tPk:                   contact.Pk,\n\t\t\tPublicRendezvousSeed: contact.PublicRendezvousSeed,\n\t\t\tMetadata:             contact.Metadata,\n\t\t},\n\t\tOwnMetadata: ownMetadata,\n\t}, protocoltypes.EventType_EventTypeAccountContactRequestOutgoingEnqueued)\n\n\tm.logger.Debug(\"Enqueued contact request\", tyber.FormatStepLogFields(ctx, []tyber.Detail{})...)\n\n\treturn op, err\n}\n\n// ContactRequestOutgoingSent indicates the payload includes that the deviceKeystore has sent a contact request\nfunc (m *MetadataStore) ContactRequestOutgoingSent(ctx context.Context, pk crypto.PubKey) (operation.Operation, error) {\n\tif !m.typeChecker(isAccountGroup) {\n\t\treturn nil, errcode.ErrCode_ErrGroupInvalidType\n\t}\n\n\tswitch m.getContactStatus(pk) {\n\tcase protocoltypes.ContactState_ContactStateToRequest:\n\tcase protocoltypes.ContactState_ContactStateReceived:\n\tcase protocoltypes.ContactState_ContactStateRemoved:\n\tcase protocoltypes.ContactState_ContactStateDiscarded:\n\n\tcase protocoltypes.ContactState_ContactStateUndefined:\n\t\treturn nil, errcode.ErrCode_ErrContactRequestContactUndefined\n\tcase protocoltypes.ContactState_ContactStateAdded:\n\t\treturn nil, errcode.ErrCode_ErrContactRequestContactAlreadyAdded\n\tcase protocoltypes.ContactState_ContactStateBlocked:\n\t\treturn nil, errcode.ErrCode_ErrContactRequestContactBlocked\n\tdefault:\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput\n\t}\n\n\treturn m.contactAction(ctx, pk, &protocoltypes.AccountContactRequestOutgoingSent{}, protocoltypes.EventType_EventTypeAccountContactRequestOutgoingSent)\n}\n\n// ContactRequestIncomingReceived indicates the payload includes that the deviceKeystore has received a contact request\nfunc (m *MetadataStore) ContactRequestIncomingReceived(ctx context.Context, contact *protocoltypes.ShareableContact) (operation.Operation, error) {\n\tm.logger.Debug(\"Sending ContactRequestIncomingReceived on Account group\", tyber.FormatStepLogFields(ctx, []tyber.Detail{})...)\n\n\tif !m.typeChecker(isAccountGroup) {\n\t\treturn nil, errcode.ErrCode_ErrGroupInvalidType\n\t}\n\n\tif err := contact.CheckFormat(protocoltypes.ShareableContactOptionsAllowMissingRDVSeed); err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(err)\n\t}\n\n\taccountPublicKey := m.memberDevice.Member()\n\tif contact.IsSamePK(accountPublicKey) {\n\t\treturn nil, errcode.ErrCode_ErrContactRequestSameAccount\n\t}\n\n\tpk, err := contact.GetPubKey()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\tswitch m.getContactStatus(pk) {\n\tcase protocoltypes.ContactState_ContactStateUndefined:\n\tcase protocoltypes.ContactState_ContactStateRemoved:\n\tcase protocoltypes.ContactState_ContactStateDiscarded:\n\n\t// If incoming request comes from an account for which an outgoing request\n\t// is in \"sending\" state, mark the outgoing request as \"sent\"\n\tcase protocoltypes.ContactState_ContactStateToRequest:\n\t\treturn m.ContactRequestOutgoingSent(ctx, pk)\n\n\t// Errors\n\tcase protocoltypes.ContactState_ContactStateReceived:\n\t\treturn nil, errcode.ErrCode_ErrContactRequestIncomingAlreadyReceived\n\tcase protocoltypes.ContactState_ContactStateAdded:\n\t\treturn nil, errcode.ErrCode_ErrContactRequestContactAlreadyAdded\n\tcase protocoltypes.ContactState_ContactStateBlocked:\n\t\treturn nil, errcode.ErrCode_ErrContactRequestContactBlocked\n\tdefault:\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput\n\t}\n\n\treturn m.attributeSignAndAddEvent(ctx, &protocoltypes.AccountContactRequestIncomingReceived{\n\t\tContactPk:             contact.Pk,\n\t\tContactRendezvousSeed: contact.PublicRendezvousSeed,\n\t\tContactMetadata:       contact.Metadata,\n\t}, protocoltypes.EventType_EventTypeAccountContactRequestIncomingReceived)\n}\n\n// ContactRequestIncomingDiscard indicates the payload includes that the deviceKeystore has ignored a contact request\nfunc (m *MetadataStore) ContactRequestIncomingDiscard(ctx context.Context, pk crypto.PubKey) (operation.Operation, error) {\n\tif !m.typeChecker(isAccountGroup) {\n\t\treturn nil, errcode.ErrCode_ErrGroupInvalidType\n\t}\n\n\tif !m.checkContactStatus(pk, protocoltypes.ContactState_ContactStateReceived) {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput\n\t}\n\n\treturn m.contactAction(ctx, pk, &protocoltypes.AccountContactRequestIncomingDiscarded{}, protocoltypes.EventType_EventTypeAccountContactRequestIncomingDiscarded)\n}\n\n// ContactRequestIncomingAccept indicates the payload includes that the deviceKeystore has accepted a contact request\nfunc (m *MetadataStore) ContactRequestIncomingAccept(ctx context.Context, pk crypto.PubKey) (operation.Operation, error) {\n\tif !m.typeChecker(isAccountGroup) {\n\t\treturn nil, errcode.ErrCode_ErrGroupInvalidType\n\t}\n\n\tif !m.checkContactStatus(pk, protocoltypes.ContactState_ContactStateReceived) {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput\n\t}\n\n\treturn m.contactAction(ctx, pk, &protocoltypes.AccountContactRequestIncomingAccepted{}, protocoltypes.EventType_EventTypeAccountContactRequestIncomingAccepted)\n}\n\n// ContactBlock indicates the payload includes that the deviceKeystore has blocked a contact\nfunc (m *MetadataStore) ContactBlock(ctx context.Context, pk crypto.PubKey) (operation.Operation, error) {\n\tif !m.typeChecker(isAccountGroup) {\n\t\treturn nil, errcode.ErrCode_ErrGroupInvalidType\n\t}\n\n\taccountPublicKey := m.memberDevice.Member()\n\tif accountPublicKey.Equals(pk) {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput\n\t}\n\n\tif m.checkContactStatus(pk, protocoltypes.ContactState_ContactStateBlocked) {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput\n\t}\n\n\treturn m.contactAction(ctx, pk, &protocoltypes.AccountContactBlocked{}, protocoltypes.EventType_EventTypeAccountContactBlocked)\n}\n\n// ContactUnblock indicates the payload includes that the deviceKeystore has unblocked a contact\nfunc (m *MetadataStore) ContactUnblock(ctx context.Context, pk crypto.PubKey) (operation.Operation, error) {\n\tif !m.typeChecker(isAccountGroup) {\n\t\treturn nil, errcode.ErrCode_ErrGroupInvalidType\n\t}\n\n\tif !m.checkContactStatus(pk, protocoltypes.ContactState_ContactStateBlocked) {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput\n\t}\n\n\treturn m.contactAction(ctx, pk, &protocoltypes.AccountContactUnblocked{}, protocoltypes.EventType_EventTypeAccountContactUnblocked)\n}\n\nfunc (m *MetadataStore) ContactSendAliasKey(ctx context.Context) (operation.Operation, error) {\n\tif !m.typeChecker(isContactGroup) {\n\t\treturn nil, errcode.ErrCode_ErrGroupInvalidType\n\t}\n\n\taccountProofPublicKey, err := m.secretStore.GetAccountProofPublicKey()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\n\talias, err := accountProofPublicKey.Raw()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInternal.Wrap(err)\n\t}\n\n\treturn m.attributeSignAndAddEvent(ctx, &protocoltypes.ContactAliasKeyAdded{\n\t\tAliasPk: alias,\n\t}, protocoltypes.EventType_EventTypeContactAliasKeyAdded)\n}\n\nfunc (m *MetadataStore) SendAliasProof(ctx context.Context) (operation.Operation, error) {\n\tif !m.typeChecker(isMultiMemberGroup) {\n\t\treturn nil, errcode.ErrCode_ErrGroupInvalidType\n\t}\n\n\tresolver := []byte(nil) // TODO: should be a hmac value of something for quicker searches\n\tproof := []byte(nil)    // TODO: should be a signed value of something\n\n\treturn m.attributeSignAndAddEvent(ctx, &protocoltypes.MultiMemberGroupAliasResolverAdded{\n\t\tAliasResolver: resolver,\n\t\tAliasProof:    proof,\n\t}, protocoltypes.EventType_EventTypeMultiMemberGroupAliasResolverAdded)\n}\n\nfunc (m *MetadataStore) SendAppMetadata(ctx context.Context, message []byte) (operation.Operation, error) {\n\treturn m.attributeSignAndAddEvent(ctx, &protocoltypes.GroupMetadataPayloadSent{\n\t\tMessage: message,\n\t}, protocoltypes.EventType_EventTypeGroupMetadataPayloadSent)\n}\n\nfunc (m *MetadataStore) SendAccountVerifiedCredentialAdded(ctx context.Context, token *protocoltypes.AccountVerifiedCredentialRegistered) (operation.Operation, error) {\n\tif !m.typeChecker(isAccountGroup) {\n\t\treturn nil, errcode.ErrCode_ErrGroupInvalidType\n\t}\n\n\treturn m.attributeSignAndAddEvent(ctx, token, protocoltypes.EventType_EventTypeAccountVerifiedCredentialRegistered)\n}\n\nfunc (m *MetadataStore) SendGroupReplicating(ctx context.Context, authenticationURL, replicationServer string) (operation.Operation, error) {\n\treturn m.attributeSignAndAddEvent(ctx, &protocoltypes.GroupReplicating{\n\t\tAuthenticationUrl: authenticationURL,\n\t\tReplicationServer: replicationServer,\n\t}, protocoltypes.EventType_EventTypeGroupReplicating)\n}\n\ntype accountSignableEvent interface {\n\tproto.Message\n\tSetDevicePK([]byte)\n}\n\ntype accountContactEvent interface {\n\taccountSignableEvent\n\tSetContactPK([]byte)\n}\n\ntype accountGroupEvent interface {\n\taccountSignableEvent\n\tSetGroupPK([]byte)\n}\n\nfunc (m *MetadataStore) attributeSignAndAddEvent(ctx context.Context, evt accountSignableEvent, eventType protocoltypes.EventType) (operation.Operation, error) {\n\tevt.SetDevicePK(m.devicePublicKeyRaw)\n\n\tsig, err := signProtoWithDevice(evt, m.memberDevice)\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrCryptoSignature.Wrap(err)\n\t}\n\n\tm.logger.Debug(\"Signed event\", tyber.FormatStepLogFields(ctx, []tyber.Detail{{Name: \"Signature\", Description: base64.RawURLEncoding.EncodeToString(sig)}})...)\n\n\treturn metadataStoreAddEvent(ctx, m, m.group, eventType, evt, sig)\n}\n\nfunc (m *MetadataStore) contactAction(ctx context.Context, pk crypto.PubKey, event accountContactEvent, evtType protocoltypes.EventType) (operation.Operation, error) {\n\tctx, newTrace := tyber.ContextWithTraceID(ctx)\n\tvar tyberFields []zap.Field\n\tif newTrace {\n\t\ttyberFields = tyber.FormatTraceLogFields(ctx)\n\t} else {\n\t\ttyberFields = tyber.FormatStepLogFields(ctx, []tyber.Detail{})\n\t}\n\tm.logger.Debug(\"Sending \"+strings.TrimPrefix(evtType.String(), \"EventType\")+\" on Account group\", tyberFields...)\n\n\tif pk == nil || event == nil {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput\n\t}\n\n\tpkBytes, err := pk.Raw()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\tevent.SetContactPK(pkBytes)\n\n\top, err := m.attributeSignAndAddEvent(ctx, event, evtType)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif newTrace {\n\t\tm.logger.Debug(\"Event added successfully\", tyber.FormatStepLogFields(ctx, []tyber.Detail{}, tyber.EndTrace)...)\n\t}\n\treturn op, nil\n}\n\nfunc (m *MetadataStore) groupAction(ctx context.Context, pk crypto.PubKey, event accountGroupEvent, evtType protocoltypes.EventType) (operation.Operation, error) {\n\tpkBytes, err := pk.Raw()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\tevent.SetGroupPK(pkBytes)\n\n\treturn m.attributeSignAndAddEvent(ctx, event, evtType)\n}\n\nfunc (m *MetadataStore) getContactStatus(pk crypto.PubKey) protocoltypes.ContactState {\n\tif pk == nil {\n\t\treturn protocoltypes.ContactState_ContactStateUndefined\n\t}\n\n\tcontact, err := m.Index().(*metadataStoreIndex).getContact(pk)\n\tif err != nil {\n\t\tm.logger.Warn(\"unable to get contact for public key\", zap.Error(err))\n\t\treturn protocoltypes.ContactState_ContactStateUndefined\n\t}\n\n\treturn contact.state\n}\n\nfunc (m *MetadataStore) checkContactStatus(pk crypto.PubKey, states ...protocoltypes.ContactState) bool {\n\tcontactStatus := m.getContactStatus(pk)\n\n\treturn slices.Contains(states, contactStatus)\n}\n\ntype EventMetadataReceived struct {\n\tMetaEvent *protocoltypes.GroupMetadataEvent\n\tEvent     proto.Message\n}\n\nfunc constructorFactoryGroupMetadata(s *WeshOrbitDB, logger *zap.Logger) iface.StoreConstructor {\n\treturn func(ipfs coreiface.CoreAPI, identity *identityprovider.Identity, addr address.Address, options *iface.NewStoreOptions) (iface.Store, error) {\n\t\tg, err := s.getGroupFromOptions(options)\n\t\tif err != nil {\n\t\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(err)\n\t\t}\n\t\tshortGroupType := strings.TrimPrefix(g.GetGroupType().String(), \"GroupType\")\n\t\tb64GroupPK := base64.RawURLEncoding.EncodeToString(g.PublicKey)\n\n\t\treplication := false\n\n\t\tif options.EventBus == nil {\n\t\t\toptions.EventBus = eventbus.NewBus()\n\t\t}\n\n\t\tstore := &MetadataStore{\n\t\t\teventBus:    options.EventBus,\n\t\t\tgroup:       g,\n\t\t\tlogger:      logger,\n\t\t\tsecretStore: s.secretStore,\n\t\t}\n\n\t\tif s.replicationMode {\n\t\t\treplication = true\n\t\t} else {\n\t\t\tvar err error\n\n\t\t\tstore.memberDevice, err = s.secretStore.GetOwnMemberDeviceForGroup(g)\n\t\t\tif err != nil {\n\t\t\t\tif errcode.Is(err, errcode.ErrCode_ErrInvalidInput) {\n\t\t\t\t\treplication = true\n\t\t\t\t} else {\n\t\t\t\t\treturn nil, errcode.ErrCode_ErrOrbitDBInit.Wrap(err)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tstore.devicePublicKeyRaw, err = store.memberDevice.Device().Raw()\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, errcode.ErrCode_ErrOrbitDBInit.Wrap(err)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tstore.ctx, store.cancel = context.WithCancel(context.Background())\n\n\t\tif err := store.initEmitter(); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"unable to init emitters: %w\", err)\n\t\t}\n\n\t\tif replication {\n\t\t\toptions.Index = basestore.NewNoopIndex\n\t\t\tif err := store.InitBaseStore(ipfs, identity, addr, options); err != nil {\n\t\t\t\tstore.cancel()\n\t\t\t\treturn nil, errcode.ErrCode_ErrOrbitDBInit.Wrap(err)\n\t\t\t}\n\n\t\t\treturn store, nil\n\t\t}\n\n\t\tchSub, err := store.eventBus.Subscribe([]any{\n\t\t\tnew(stores.EventWrite),\n\t\t\tnew(stores.EventReplicated),\n\t\t}, eventbus.BufSize(128))\n\t\tif err != nil {\n\t\t\tstore.cancel()\n\t\t\treturn nil, fmt.Errorf(\"unable to subscribe to store events\")\n\t\t}\n\n\t\t// Enable logs in the metadata index\n\t\tstore.setLogger(logger)\n\n\t\tgo func(ctx context.Context) {\n\t\t\tdefer chSub.Close()\n\n\t\t\tfor {\n\t\t\t\tvar e any\n\t\t\t\tselect {\n\t\t\t\tcase e = <-chSub.Out():\n\t\t\t\tcase <-ctx.Done():\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tvar entries []ipfslog.Entry\n\n\t\t\t\tswitch evt := e.(type) {\n\t\t\t\tcase stores.EventWrite:\n\t\t\t\t\tentries = []ipfslog.Entry{evt.Entry}\n\n\t\t\t\tcase stores.EventReplicated:\n\t\t\t\t\tentries = evt.Entries\n\t\t\t\t}\n\n\t\t\t\tfor _, entry := range entries {\n\t\t\t\t\tctx = tyber.ContextWithConstantTraceID(ctx, \"msgrcvd-\"+entry.GetHash().String())\n\t\t\t\t\ttyber.LogTraceStart(ctx, store.logger, fmt.Sprintf(\"Received metadata from %s group %s\", shortGroupType, b64GroupPK))\n\n\t\t\t\t\tmetaEvent, event, err := openMetadataEntry(store.OpLog(), entry, g)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t_ = tyber.LogFatalError(ctx, store.logger, \"Unable to open metadata event\", err, tyber.WithDetail(\"RawEvent\", fmt.Sprint(e)), tyber.ForceReopen)\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\n\t\t\t\t\ttyber.LogStep(ctx, store.logger, \"Opened metadata store event\",\n\t\t\t\t\t\ttyber.ForceReopen,\n\t\t\t\t\t\ttyber.EndTrace,\n\t\t\t\t\t\ttyber.WithJSONDetail(\"MetaEvent\", metaEvent),\n\t\t\t\t\t\ttyber.WithJSONDetail(\"Event\", event),\n\t\t\t\t\t\ttyber.UpdateTraceName(fmt.Sprintf(\"Received %s from %s group %s\", strings.TrimPrefix(metaEvent.GetMetadata().GetEventType().String(), \"EventType\"), shortGroupType, b64GroupPK)),\n\t\t\t\t\t)\n\n\t\t\t\t\trecvEvent := EventMetadataReceived{\n\t\t\t\t\t\tMetaEvent: metaEvent,\n\t\t\t\t\t\tEvent:     event,\n\t\t\t\t\t}\n\n\t\t\t\t\tif err := store.emitters.metadataReceived.Emit(recvEvent); err != nil {\n\t\t\t\t\t\tstore.logger.Warn(\"unable to emit recv event\", zap.Error(err))\n\t\t\t\t\t}\n\n\t\t\t\t\tif err := store.emitters.groupMetadata.Emit(metaEvent); err != nil {\n\t\t\t\t\t\tstore.logger.Warn(\"unable to emit group metadata event\", zap.Error(err))\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}(store.ctx)\n\n\t\toptions.Index = newMetadataIndex(store.ctx, g, store.memberDevice, s.secretStore)\n\t\tif err := store.InitBaseStore(ipfs, identity, addr, options); err != nil {\n\t\t\tstore.cancel()\n\t\t\treturn nil, errcode.ErrCode_ErrOrbitDBInit.Wrap(err)\n\t\t}\n\n\t\treturn store, nil\n\t}\n}\n\nfunc (m *MetadataStore) initEmitter() (err error) {\n\tif m.emitters.metadataReceived, err = m.eventBus.Emitter(new(EventMetadataReceived)); err != nil {\n\t\treturn err\n\t}\n\n\tif m.emitters.groupMetadata, err = m.eventBus.Emitter(new(*protocoltypes.GroupMetadataEvent)); err != nil {\n\t\treturn err\n\t}\n\n\treturn err\n}\n\nfunc genNewSeed() (seed []byte, err error) {\n\tseed, err = io.ReadAll(io.LimitReader(crand.Reader, protocoltypes.RendezvousSeedLength))\n\treturn seed, err\n}\n\nfunc (m *MetadataStore) Close() error {\n\tm.cancel()\n\treturn m.BaseStore.Close()\n}\n"
  },
  {
    "path": "store_metadata_index.go",
    "content": "package weshnet\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"sync\"\n\n\t\"github.com/libp2p/go-libp2p/core/crypto\"\n\t\"go.uber.org/zap\"\n\t\"google.golang.org/protobuf/proto\"\n\n\tipfslog \"berty.tech/go-ipfs-log\"\n\t\"berty.tech/go-orbit-db/iface\"\n\t\"berty.tech/weshnet/v2/pkg/cryptoutil\"\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n\t\"berty.tech/weshnet/v2/pkg/secretstore\"\n)\n\n// FIXME: replace members, devices, sentSecrets, contacts and groups by a circular buffer to avoid an attack by RAM saturation\ntype metadataStoreIndex struct {\n\tmembers                  map[string][]secretstore.MemberDevice\n\tdevices                  map[string]secretstore.MemberDevice\n\thandledEvents            map[string]struct{}\n\tsentSecrets              map[string]struct{}\n\tadmins                   map[crypto.PubKey]struct{}\n\tcontacts                 map[string]*AccountContact\n\tcontactsFromGroupPK      map[string]*AccountContact\n\tgroups                   map[string]*accountGroup\n\tcontactRequestMetadata   map[string][]byte\n\tverifiedCredentials      []*protocoltypes.AccountVerifiedCredentialRegistered\n\tcontactRequestSeed       []byte\n\tcontactRequestEnabled    *bool\n\teventHandlers            map[protocoltypes.EventType][]func(event proto.Message) error\n\tpostIndexActions         []func() error\n\teventsContactAddAliasKey []*protocoltypes.ContactAliasKeyAdded\n\townAliasKeySent          bool\n\totherAliasKey            []byte\n\tgroup                    *protocoltypes.Group\n\townMemberDevice          secretstore.MemberDevice\n\tsecretStore              secretstore.SecretStore\n\tctx                      context.Context\n\tlock                     sync.RWMutex\n\tlogger                   *zap.Logger\n}\n\n//nolint:revive\nfunc (m *metadataStoreIndex) Get(key string) any {\n\treturn nil\n}\n\nfunc (m *metadataStoreIndex) setLogger(logger *zap.Logger) {\n\tif logger == nil {\n\t\treturn\n\t}\n\n\tm.logger = logger\n}\n\nfunc (m *metadataStoreIndex) UpdateIndex(log ipfslog.Log, _ []ipfslog.Entry) error {\n\tm.lock.Lock()\n\tdefer m.lock.Unlock()\n\n\tentries := log.GetEntries().Slice()\n\n\t// Resetting state\n\tm.contacts = map[string]*AccountContact{}\n\tm.contactsFromGroupPK = map[string]*AccountContact{}\n\tm.groups = map[string]*accountGroup{}\n\tm.contactRequestMetadata = map[string][]byte{}\n\tm.contactRequestEnabled = nil\n\tm.contactRequestSeed = []byte(nil)\n\tm.verifiedCredentials = nil\n\tm.handledEvents = map[string]struct{}{}\n\n\tfor i := len(entries) - 1; i >= 0; i-- {\n\t\te := entries[i]\n\n\t\t_, alreadyHandledEvent := m.handledEvents[e.GetHash().String()]\n\n\t\t// TODO: improve account events handling\n\t\tif m.group.GroupType != protocoltypes.GroupType_GroupTypeAccount && alreadyHandledEvent {\n\t\t\tcontinue\n\t\t}\n\n\t\tmetaEvent, event, err := openMetadataEntry(log, e, m.group)\n\t\tif err != nil {\n\t\t\tm.logger.Error(\"unable to open metadata entry\", zap.Error(err))\n\t\t\tcontinue\n\t\t}\n\n\t\thandlers, ok := m.eventHandlers[metaEvent.Metadata.EventType]\n\t\tif !ok {\n\t\t\tm.handledEvents[e.GetHash().String()] = struct{}{}\n\t\t\tm.logger.Error(\"handler for event type not found\", zap.String(\"event-type\", metaEvent.Metadata.EventType.String()))\n\t\t\tcontinue\n\t\t}\n\n\t\tvar lastErr error\n\n\t\tfor _, h := range handlers {\n\t\t\terr = h(event)\n\t\t\tif err != nil {\n\t\t\t\tm.logger.Error(\"unable to handle event\", zap.Error(err))\n\t\t\t\tlastErr = err\n\t\t\t}\n\t\t}\n\n\t\tif lastErr != nil {\n\t\t\tm.handledEvents[e.GetHash().String()] = struct{}{}\n\t\t\tcontinue\n\t\t}\n\n\t\tm.handledEvents[e.GetHash().String()] = struct{}{}\n\t}\n\n\tfor _, h := range m.postIndexActions {\n\t\tif err := h(); err != nil {\n\t\t\treturn errcode.ErrCode_ErrInternal.Wrap(err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (m *metadataStoreIndex) handleGroupMemberDeviceAdded(event proto.Message) error {\n\te, ok := event.(*protocoltypes.GroupMemberDeviceAdded)\n\tif !ok {\n\t\treturn errcode.ErrCode_ErrInvalidInput\n\t}\n\n\tmember, err := crypto.UnmarshalEd25519PublicKey(e.MemberPk)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\tdevice, err := crypto.UnmarshalEd25519PublicKey(e.DevicePk)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\tif _, ok := m.devices[string(e.DevicePk)]; ok {\n\t\treturn nil\n\t}\n\n\tmemberDevice := secretstore.NewMemberDevice(member, device)\n\n\tm.devices[string(e.DevicePk)] = memberDevice\n\tm.members[string(e.MemberPk)] = append(m.members[string(e.MemberPk)], memberDevice)\n\n\treturn nil\n}\n\nfunc (m *metadataStoreIndex) handleGroupDeviceChainKeyAdded(event proto.Message) error {\n\te, ok := event.(*protocoltypes.GroupDeviceChainKeyAdded)\n\tif !ok {\n\t\treturn errcode.ErrCode_ErrInvalidInput\n\t}\n\n\t_, err := crypto.UnmarshalEd25519PublicKey(e.DestMemberPk)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\tsenderPK, err := crypto.UnmarshalEd25519PublicKey(e.DevicePk)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\tif m.ownMemberDevice.Device().Equals(senderPK) {\n\t\tm.sentSecrets[string(e.DestMemberPk)] = struct{}{}\n\t}\n\n\treturn nil\n}\n\nfunc (m *metadataStoreIndex) getMemberByDevice(devicePublicKey crypto.PubKey) (crypto.PubKey, error) {\n\tm.lock.RLock()\n\tdefer m.lock.RUnlock()\n\n\tpublicKeyBytes, err := devicePublicKey.Raw()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(err)\n\t}\n\n\treturn m.unsafeGetMemberByDevice(publicKeyBytes)\n}\n\nfunc (m *metadataStoreIndex) unsafeGetMemberByDevice(publicKeyBytes []byte) (crypto.PubKey, error) {\n\tif l := len(publicKeyBytes); l != cryptoutil.KeySize {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"invalid private key size, expected %d got %d\", cryptoutil.KeySize, l))\n\t}\n\n\tdevice, ok := m.devices[string(publicKeyBytes)]\n\tif !ok {\n\t\treturn nil, errcode.ErrCode_ErrMissingInput\n\t}\n\n\treturn device.Member(), nil\n}\n\nfunc (m *metadataStoreIndex) getDevicesForMember(pk crypto.PubKey) ([]crypto.PubKey, error) {\n\tm.lock.RLock()\n\tdefer m.lock.RUnlock()\n\n\tid, err := pk.Raw()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput.Wrap(err)\n\t}\n\n\tmds, ok := m.members[string(id)]\n\tif !ok {\n\t\treturn nil, errcode.ErrCode_ErrInvalidInput\n\t}\n\n\tret := make([]crypto.PubKey, len(mds))\n\tfor i, md := range mds {\n\t\tret[i] = md.Device()\n\t}\n\n\treturn ret, nil\n}\n\nfunc (m *metadataStoreIndex) MemberCount() int {\n\tm.lock.RLock()\n\tdefer m.lock.RUnlock()\n\n\treturn len(m.members)\n}\n\nfunc (m *metadataStoreIndex) DeviceCount() int {\n\tm.lock.RLock()\n\tdefer m.lock.RUnlock()\n\n\treturn len(m.devices)\n}\n\nfunc (m *metadataStoreIndex) listContacts() map[string]*AccountContact {\n\tm.lock.RLock()\n\tdefer m.lock.RUnlock()\n\n\tcontacts := make(map[string]*AccountContact)\n\n\tfor k, contact := range m.contacts {\n\t\tcontacts[k] = &AccountContact{\n\t\t\tstate: contact.state,\n\t\t\tcontact: &protocoltypes.ShareableContact{\n\t\t\t\tPk:                   contact.contact.Pk,\n\t\t\t\tPublicRendezvousSeed: contact.contact.PublicRendezvousSeed,\n\t\t\t\tMetadata:             contact.contact.Metadata,\n\t\t\t},\n\t\t}\n\t}\n\n\treturn contacts\n}\n\nfunc (m *metadataStoreIndex) listVerifiedCredentials() []*protocoltypes.AccountVerifiedCredentialRegistered {\n\tm.lock.RLock()\n\tdefer m.lock.RUnlock()\n\n\treturn m.verifiedCredentials\n}\n\nfunc (m *metadataStoreIndex) listMembers() []crypto.PubKey {\n\tm.lock.RLock()\n\tdefer m.lock.RUnlock()\n\n\tmembers := make([]crypto.PubKey, len(m.members))\n\ti := 0\n\n\tfor _, md := range m.members {\n\t\tmembers[i] = md[0].Member()\n\t\ti++\n\t}\n\n\treturn members\n}\n\nfunc (m *metadataStoreIndex) listDevices() []crypto.PubKey {\n\tm.lock.RLock()\n\tdefer m.lock.RUnlock()\n\n\tdevices := make([]crypto.PubKey, len(m.devices))\n\ti := 0\n\n\tfor _, md := range m.devices {\n\t\tdevices[i] = md.Device()\n\t\ti++\n\t}\n\n\treturn devices\n}\n\nfunc (m *metadataStoreIndex) areSecretsAlreadySent(pk crypto.PubKey) (bool, error) {\n\tm.lock.RLock()\n\tdefer m.lock.RUnlock()\n\n\tkey, err := pk.Raw()\n\tif err != nil {\n\t\treturn false, errcode.ErrCode_ErrInvalidInput.Wrap(err)\n\t}\n\n\t_, ok := m.sentSecrets[string(key)]\n\treturn ok, nil\n}\n\ntype accountGroupJoinedState uint32\n\nconst (\n\taccountGroupJoinedStateJoined accountGroupJoinedState = iota + 1\n\taccountGroupJoinedStateLeft\n)\n\ntype accountGroup struct {\n\tstate accountGroupJoinedState\n\tgroup *protocoltypes.Group\n}\n\ntype AccountContact struct {\n\tstate   protocoltypes.ContactState\n\tcontact *protocoltypes.ShareableContact\n}\n\nfunc (m *metadataStoreIndex) handleGroupJoined(event proto.Message) error {\n\tevt, ok := event.(*protocoltypes.AccountGroupJoined)\n\tif !ok {\n\t\treturn errcode.ErrCode_ErrInvalidInput\n\t}\n\n\t_, ok = m.groups[string(evt.Group.PublicKey)]\n\tif ok {\n\t\treturn nil\n\t}\n\n\tm.groups[string(evt.Group.PublicKey)] = &accountGroup{\n\t\tgroup: evt.Group,\n\t\tstate: accountGroupJoinedStateJoined,\n\t}\n\n\treturn nil\n}\n\nfunc (m *metadataStoreIndex) handleGroupLeft(event proto.Message) error {\n\tevt, ok := event.(*protocoltypes.AccountGroupLeft)\n\tif !ok {\n\t\treturn errcode.ErrCode_ErrInvalidInput\n\t}\n\n\t_, ok = m.groups[string(evt.GroupPk)]\n\tif ok {\n\t\treturn nil\n\t}\n\n\tm.groups[string(evt.GroupPk)] = &accountGroup{\n\t\tstate: accountGroupJoinedStateLeft,\n\t}\n\n\treturn nil\n}\n\nfunc (m *metadataStoreIndex) handleContactRequestDisabled(event proto.Message) error {\n\tif m.contactRequestEnabled != nil {\n\t\treturn nil\n\t}\n\n\t_, ok := event.(*protocoltypes.AccountContactRequestDisabled)\n\tif !ok {\n\t\treturn errcode.ErrCode_ErrInvalidInput\n\t}\n\n\tf := false\n\tm.contactRequestEnabled = &f\n\n\treturn nil\n}\n\nfunc (m *metadataStoreIndex) handleContactRequestEnabled(event proto.Message) error {\n\tif m.contactRequestEnabled != nil {\n\t\treturn nil\n\t}\n\n\t_, ok := event.(*protocoltypes.AccountContactRequestEnabled)\n\tif !ok {\n\t\treturn errcode.ErrCode_ErrInvalidInput\n\t}\n\n\tt := true\n\tm.contactRequestEnabled = &t\n\n\treturn nil\n}\n\nfunc (m *metadataStoreIndex) handleContactRequestReferenceReset(event proto.Message) error {\n\tevt, ok := event.(*protocoltypes.AccountContactRequestReferenceReset)\n\tif !ok {\n\t\treturn errcode.ErrCode_ErrInvalidInput\n\t}\n\n\tif m.contactRequestSeed != nil {\n\t\treturn nil\n\t}\n\n\tm.contactRequestSeed = evt.PublicRendezvousSeed\n\n\treturn nil\n}\n\nfunc (m *metadataStoreIndex) registerContactFromGroupPK(ac *AccountContact) error {\n\tif m.group.GroupType != protocoltypes.GroupType_GroupTypeAccount {\n\t\treturn errcode.ErrCode_ErrGroupInvalidType\n\t}\n\n\tcontactPK, err := crypto.UnmarshalEd25519PublicKey(ac.contact.Pk)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\tgroup, err := m.secretStore.GetGroupForContact(contactPK)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrOrbitDBOpen.Wrap(err)\n\t}\n\n\tm.contactsFromGroupPK[string(group.PublicKey)] = ac\n\n\treturn nil\n}\n\nfunc (m *metadataStoreIndex) handleContactRequestOutgoingEnqueued(event proto.Message) error {\n\tevt, ok := event.(*protocoltypes.AccountContactRequestOutgoingEnqueued)\n\tif ko := !ok || evt.Contact == nil; ko {\n\t\treturn errcode.ErrCode_ErrInvalidInput\n\t}\n\n\tif _, ok := m.contacts[string(evt.Contact.Pk)]; ok {\n\t\tif m.contacts[string(evt.Contact.Pk)].contact.Metadata == nil {\n\t\t\tm.contacts[string(evt.Contact.Pk)].contact.Metadata = evt.Contact.Metadata\n\t\t}\n\n\t\tif m.contacts[string(evt.Contact.Pk)].contact.PublicRendezvousSeed == nil {\n\t\t\tm.contacts[string(evt.Contact.Pk)].contact.PublicRendezvousSeed = evt.Contact.PublicRendezvousSeed\n\t\t}\n\n\t\treturn nil\n\t}\n\n\tif data, ok := m.contactRequestMetadata[string(evt.Contact.Pk)]; !ok || len(data) == 0 {\n\t\tm.contactRequestMetadata[string(evt.Contact.Pk)] = evt.OwnMetadata\n\t}\n\n\tac := &AccountContact{\n\t\tstate: protocoltypes.ContactState_ContactStateToRequest,\n\t\tcontact: &protocoltypes.ShareableContact{\n\t\t\tPk:                   evt.Contact.Pk,\n\t\t\tMetadata:             evt.Contact.Metadata,\n\t\t\tPublicRendezvousSeed: evt.Contact.PublicRendezvousSeed,\n\t\t},\n\t}\n\n\tm.contacts[string(evt.Contact.Pk)] = ac\n\terr := m.registerContactFromGroupPK(ac)\n\n\treturn err\n}\n\nfunc (m *metadataStoreIndex) handleContactRequestOutgoingSent(event proto.Message) error {\n\tevt, ok := event.(*protocoltypes.AccountContactRequestOutgoingSent)\n\tif !ok {\n\t\treturn errcode.ErrCode_ErrInvalidInput\n\t}\n\n\tif _, ok := m.contacts[string(evt.ContactPk)]; ok {\n\t\treturn nil\n\t}\n\n\tac := &AccountContact{\n\t\tstate: protocoltypes.ContactState_ContactStateAdded,\n\t\tcontact: &protocoltypes.ShareableContact{\n\t\t\tPk: evt.ContactPk,\n\t\t},\n\t}\n\n\tm.contacts[string(evt.ContactPk)] = ac\n\terr := m.registerContactFromGroupPK(ac)\n\n\treturn err\n}\n\nfunc (m *metadataStoreIndex) handleContactRequestIncomingReceived(event proto.Message) error {\n\tevt, ok := event.(*protocoltypes.AccountContactRequestIncomingReceived)\n\tif !ok {\n\t\treturn errcode.ErrCode_ErrInvalidInput\n\t}\n\n\tif _, ok := m.contacts[string(evt.ContactPk)]; ok {\n\t\tif m.contacts[string(evt.ContactPk)].contact.Metadata == nil {\n\t\t\tm.contacts[string(evt.ContactPk)].contact.Metadata = evt.ContactMetadata\n\t\t}\n\n\t\tif m.contacts[string(evt.ContactPk)].contact.PublicRendezvousSeed == nil {\n\t\t\tm.contacts[string(evt.ContactPk)].contact.PublicRendezvousSeed = evt.ContactRendezvousSeed\n\t\t}\n\n\t\treturn nil\n\t}\n\n\tac := &AccountContact{\n\t\tstate: protocoltypes.ContactState_ContactStateReceived,\n\t\tcontact: &protocoltypes.ShareableContact{\n\t\t\tPk:                   evt.ContactPk,\n\t\t\tMetadata:             evt.ContactMetadata,\n\t\t\tPublicRendezvousSeed: evt.ContactRendezvousSeed,\n\t\t},\n\t}\n\n\tm.contacts[string(evt.ContactPk)] = ac\n\terr := m.registerContactFromGroupPK(ac)\n\n\treturn err\n}\n\nfunc (m *metadataStoreIndex) handleContactRequestIncomingDiscarded(event proto.Message) error {\n\tevt, ok := event.(*protocoltypes.AccountContactRequestIncomingDiscarded)\n\tif !ok {\n\t\treturn errcode.ErrCode_ErrInvalidInput\n\t}\n\n\tif _, ok := m.contacts[string(evt.ContactPk)]; ok {\n\t\treturn nil\n\t}\n\n\tac := &AccountContact{\n\t\tstate: protocoltypes.ContactState_ContactStateDiscarded,\n\t\tcontact: &protocoltypes.ShareableContact{\n\t\t\tPk: evt.ContactPk,\n\t\t},\n\t}\n\n\tm.contacts[string(evt.ContactPk)] = ac\n\terr := m.registerContactFromGroupPK(ac)\n\n\treturn err\n}\n\nfunc (m *metadataStoreIndex) handleContactRequestIncomingAccepted(event proto.Message) error {\n\tevt, ok := event.(*protocoltypes.AccountContactRequestIncomingAccepted)\n\tif !ok {\n\t\treturn errcode.ErrCode_ErrInvalidInput\n\t}\n\n\tif _, ok := m.contacts[string(evt.ContactPk)]; ok {\n\t\treturn nil\n\t}\n\n\tac := &AccountContact{\n\t\tstate: protocoltypes.ContactState_ContactStateAdded,\n\t\tcontact: &protocoltypes.ShareableContact{\n\t\t\tPk: evt.ContactPk,\n\t\t},\n\t}\n\n\tm.contacts[string(evt.ContactPk)] = ac\n\terr := m.registerContactFromGroupPK(ac)\n\n\treturn err\n}\n\nfunc (m *metadataStoreIndex) handleContactBlocked(event proto.Message) error {\n\tevt, ok := event.(*protocoltypes.AccountContactBlocked)\n\tif !ok {\n\t\treturn errcode.ErrCode_ErrInvalidInput\n\t}\n\n\tif _, ok := m.contacts[string(evt.ContactPk)]; ok {\n\t\treturn nil\n\t}\n\n\tac := &AccountContact{\n\t\tstate: protocoltypes.ContactState_ContactStateBlocked,\n\t\tcontact: &protocoltypes.ShareableContact{\n\t\t\tPk: evt.ContactPk,\n\t\t},\n\t}\n\n\tm.contacts[string(evt.ContactPk)] = ac\n\terr := m.registerContactFromGroupPK(ac)\n\n\treturn err\n}\n\nfunc (m *metadataStoreIndex) handleContactUnblocked(event proto.Message) error {\n\tevt, ok := event.(*protocoltypes.AccountContactUnblocked)\n\tif !ok {\n\t\treturn errcode.ErrCode_ErrInvalidInput\n\t}\n\n\tif _, ok := m.contacts[string(evt.ContactPk)]; ok {\n\t\treturn nil\n\t}\n\n\tac := &AccountContact{\n\t\tstate: protocoltypes.ContactState_ContactStateRemoved,\n\t\tcontact: &protocoltypes.ShareableContact{\n\t\t\tPk: evt.ContactPk,\n\t\t},\n\t}\n\n\tm.contacts[string(evt.ContactPk)] = ac\n\terr := m.registerContactFromGroupPK(ac)\n\n\treturn err\n}\n\nfunc (m *metadataStoreIndex) handleContactAliasKeyAdded(event proto.Message) error {\n\tevt, ok := event.(*protocoltypes.ContactAliasKeyAdded)\n\tif !ok {\n\t\treturn errcode.ErrCode_ErrInvalidInput\n\t}\n\n\tm.eventsContactAddAliasKey = append(m.eventsContactAddAliasKey, evt)\n\n\treturn nil\n}\n\nfunc (m *metadataStoreIndex) handleMultiMemberInitialMember(event proto.Message) error {\n\te, ok := event.(*protocoltypes.MultiMemberGroupInitialMemberAnnounced)\n\tif !ok {\n\t\treturn errcode.ErrCode_ErrInvalidInput\n\t}\n\n\tpk, err := crypto.UnmarshalEd25519PublicKey(e.MemberPk)\n\tif err != nil {\n\t\treturn errcode.ErrCode_ErrDeserialization.Wrap(err)\n\t}\n\n\tif _, ok := m.admins[pk]; ok {\n\t\treturn errcode.ErrCode_ErrInternal\n\t}\n\n\tm.admins[pk] = struct{}{}\n\n\treturn nil\n}\n\n//nolint:revive\nfunc (m *metadataStoreIndex) handleMultiMemberGrantAdminRole(event proto.Message) error {\n\t// TODO:\n\n\treturn nil\n}\n\nfunc (m *metadataStoreIndex) handleGroupMetadataPayloadSent(_ proto.Message) error {\n\treturn nil\n}\n\nfunc (m *metadataStoreIndex) handleAccountVerifiedCredentialRegistered(event proto.Message) error {\n\te, ok := event.(*protocoltypes.AccountVerifiedCredentialRegistered)\n\tif !ok {\n\t\treturn errcode.ErrCode_ErrInvalidInput\n\t}\n\n\tm.verifiedCredentials = append(m.verifiedCredentials, e)\n\n\treturn nil\n}\n\nfunc (m *metadataStoreIndex) listAdmins() []crypto.PubKey {\n\tm.lock.RLock()\n\tdefer m.lock.RUnlock()\n\n\tadmins := make([]crypto.PubKey, len(m.admins))\n\ti := 0\n\n\tfor admin := range m.admins {\n\t\tadmins[i] = admin\n\t\ti++\n\t}\n\n\treturn admins\n}\n\nfunc (m *metadataStoreIndex) listOtherMembersDevices() []crypto.PubKey {\n\tm.lock.RLock()\n\tdefer m.lock.RUnlock()\n\n\tif m.ownMemberDevice == nil || m.ownMemberDevice.Member() == nil {\n\t\treturn nil\n\t}\n\n\townMemberPK, err := m.ownMemberDevice.Member().Raw()\n\tif err != nil {\n\t\tm.logger.Warn(\"unable to serialize member pubkey\", zap.Error(err))\n\t\treturn nil\n\t}\n\n\tdevices := []crypto.PubKey(nil)\n\tfor pk, devicesForMember := range m.members {\n\t\tif string(ownMemberPK) == pk {\n\t\t\tcontinue\n\t\t}\n\n\t\tfor _, md := range devicesForMember {\n\t\t\tdevices = append(devices, md.Device())\n\t\t}\n\t}\n\n\treturn devices\n}\n\nfunc (m *metadataStoreIndex) contactRequestsEnabled() bool {\n\tm.lock.RLock()\n\tdefer m.lock.RUnlock()\n\n\treturn m.contactRequestEnabled != nil && *m.contactRequestEnabled\n}\n\nfunc (m *metadataStoreIndex) contactRequestsSeed() []byte {\n\tm.lock.RLock()\n\tdefer m.lock.RUnlock()\n\n\treturn m.contactRequestSeed\n}\n\nfunc (m *metadataStoreIndex) getContact(pk crypto.PubKey) (*AccountContact, error) {\n\tm.lock.RLock()\n\tdefer m.lock.RUnlock()\n\n\tbytes, err := pk.Raw()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_ErrSerialization.Wrap(err)\n\t}\n\n\tcontact, ok := m.contacts[string(bytes)]\n\tif !ok {\n\t\treturn nil, errcode.ErrCode_ErrMissingMapKey.Wrap(err)\n\t}\n\n\treturn contact, nil\n}\n\nfunc (m *metadataStoreIndex) postHandlerSentAliases() error {\n\tfor _, evt := range m.eventsContactAddAliasKey {\n\t\tmemberPublicKey, err := m.unsafeGetMemberByDevice(evt.DevicePk)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"couldn't get member for device\")\n\t\t}\n\n\t\tif memberPublicKey.Equals(m.ownMemberDevice.Member()) {\n\t\t\tm.ownAliasKeySent = true\n\t\t\tcontinue\n\t\t}\n\n\t\tif l := len(evt.AliasPk); l != cryptoutil.KeySize {\n\t\t\treturn errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf(\"invalid alias key size, expected %d, got %d\", cryptoutil.KeySize, l))\n\t\t}\n\n\t\tm.otherAliasKey = evt.AliasPk\n\t}\n\n\tm.eventsContactAddAliasKey = nil\n\n\treturn nil\n}\n\n// nolint:staticcheck,revive\n// newMetadataIndex returns a new index to manage the list of the group members\nfunc newMetadataIndex(ctx context.Context, g *protocoltypes.Group, md secretstore.MemberDevice, secretStore secretstore.SecretStore) iface.IndexConstructor {\n\treturn func(publicKey []byte) iface.StoreIndex {\n\t\tm := &metadataStoreIndex{\n\t\t\tmembers:                map[string][]secretstore.MemberDevice{},\n\t\t\tdevices:                map[string]secretstore.MemberDevice{},\n\t\t\tadmins:                 map[crypto.PubKey]struct{}{},\n\t\t\tsentSecrets:            map[string]struct{}{},\n\t\t\thandledEvents:          map[string]struct{}{},\n\t\t\tcontacts:               map[string]*AccountContact{},\n\t\t\tcontactsFromGroupPK:    map[string]*AccountContact{},\n\t\t\tgroups:                 map[string]*accountGroup{},\n\t\t\tcontactRequestMetadata: map[string][]byte{},\n\t\t\tgroup:                  g,\n\t\t\townMemberDevice:        md,\n\t\t\tsecretStore:            secretStore,\n\t\t\tctx:                    ctx,\n\t\t\tlogger:                 zap.NewNop(),\n\t\t}\n\n\t\tm.eventHandlers = map[protocoltypes.EventType][]func(event proto.Message) error{\n\t\t\tprotocoltypes.EventType_EventTypeAccountContactBlocked:                  {m.handleContactBlocked},\n\t\t\tprotocoltypes.EventType_EventTypeAccountContactRequestDisabled:          {m.handleContactRequestDisabled},\n\t\t\tprotocoltypes.EventType_EventTypeAccountContactRequestEnabled:           {m.handleContactRequestEnabled},\n\t\t\tprotocoltypes.EventType_EventTypeAccountContactRequestIncomingAccepted:  {m.handleContactRequestIncomingAccepted},\n\t\t\tprotocoltypes.EventType_EventTypeAccountContactRequestIncomingDiscarded: {m.handleContactRequestIncomingDiscarded},\n\t\t\tprotocoltypes.EventType_EventTypeAccountContactRequestIncomingReceived:  {m.handleContactRequestIncomingReceived},\n\t\t\tprotocoltypes.EventType_EventTypeAccountContactRequestOutgoingEnqueued:  {m.handleContactRequestOutgoingEnqueued},\n\t\t\tprotocoltypes.EventType_EventTypeAccountContactRequestOutgoingSent:      {m.handleContactRequestOutgoingSent},\n\t\t\tprotocoltypes.EventType_EventTypeAccountContactRequestReferenceReset:    {m.handleContactRequestReferenceReset},\n\t\t\tprotocoltypes.EventType_EventTypeAccountContactUnblocked:                {m.handleContactUnblocked},\n\t\t\tprotocoltypes.EventType_EventTypeAccountGroupJoined:                     {m.handleGroupJoined},\n\t\t\tprotocoltypes.EventType_EventTypeAccountGroupLeft:                       {m.handleGroupLeft},\n\t\t\tprotocoltypes.EventType_EventTypeContactAliasKeyAdded:                   {m.handleContactAliasKeyAdded},\n\t\t\tprotocoltypes.EventType_EventTypeGroupDeviceChainKeyAdded:               {m.handleGroupDeviceChainKeyAdded},\n\t\t\tprotocoltypes.EventType_EventTypeGroupMemberDeviceAdded:                 {m.handleGroupMemberDeviceAdded},\n\t\t\tprotocoltypes.EventType_EventTypeMultiMemberGroupAdminRoleGranted:       {m.handleMultiMemberGrantAdminRole},\n\t\t\tprotocoltypes.EventType_EventTypeMultiMemberGroupInitialMemberAnnounced: {m.handleMultiMemberInitialMember},\n\t\t\tprotocoltypes.EventType_EventTypeGroupMetadataPayloadSent:               {m.handleGroupMetadataPayloadSent},\n\t\t\tprotocoltypes.EventType_EventTypeAccountVerifiedCredentialRegistered:    {m.handleAccountVerifiedCredentialRegistered},\n\t\t}\n\n\t\tm.postIndexActions = []func() error{\n\t\t\tm.postHandlerSentAliases,\n\t\t}\n\n\t\treturn m\n\t}\n}\n\nvar _ iface.StoreIndex = &metadataStoreIndex{}\n"
  },
  {
    "path": "store_metadata_test.go",
    "content": "package weshnet\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\tcrand \"crypto/rand\"\n\t\"fmt\"\n\tmrand \"math/rand\"\n\t\"testing\"\n\t\"time\"\n\n\tdatastore \"github.com/ipfs/go-datastore\"\n\tds_sync \"github.com/ipfs/go-datastore/sync\"\n\t\"github.com/libp2p/go-libp2p/core/crypto\"\n\tmocknet \"github.com/libp2p/go-libp2p/p2p/net/mock\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/zap\"\n\n\t\"berty.tech/weshnet/v2/pkg/ipfsutil\"\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n\t\"berty.tech/weshnet/v2/pkg/testutil\"\n)\n\nfunc TestMetadataStoreSecret_Basic(t *testing.T) {\n\tt.Skip(\"skipping as we don't care about this code now\")\n\n\t// TODO: handle more cases (more members, more devices...)\n\tmemberCount := 2\n\tdeviceCount := 1\n\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\tpeers, groupSK, cleanup := CreatePeersWithGroupTest(ctx, t, \"/tmp/secrets_test\", memberCount, deviceCount)\n\tdefer cleanup()\n\n\tsecretsAdded := make(chan struct{})\n\n\tmsA := peers[0].GC.MetadataStore()\n\tmsB := peers[1].GC.MetadataStore()\n\n\tgo waitForBertyEventType(ctx, t, msA, protocoltypes.EventType_EventTypeGroupDeviceChainKeyAdded, 2, secretsAdded)\n\tgo waitForBertyEventType(ctx, t, msB, protocoltypes.EventType_EventTypeGroupDeviceChainKeyAdded, 2, secretsAdded)\n\tinviteAllPeersToGroup(ctx, t, peers, groupSK)\n\n\tdevPkA := peers[0].GC.DevicePubKey()\n\tdevPkB := peers[1].GC.DevicePubKey()\n\n\t<-secretsAdded\n\t<-secretsAdded\n\n\t_ = devPkA\n\t_ = devPkB\n}\n\nfunc TestMetadataStoreMember(t *testing.T) {\n\tt.Skip(\"skipping as we don't care about this code now\")\n\n\t// If seed is not set, it will default to 1, explicitly setting it and displaying it if the test fails\n\tseed := time.Now().UTC().UnixNano()\n\tmrand.Seed(seed)\n\n\tfor _, tc := range []struct {\n\t\tmemberCount int\n\t\tdeviceCount int\n\t\tslow        bool\n\t}{\n\t\t{memberCount: 1, deviceCount: 1, slow: false},\n\t\t{memberCount: 1, deviceCount: 3, slow: false},\n\t\t{memberCount: 3, deviceCount: 1, slow: false},\n\t\t// TODO: fix pubsub issues\n\t\t// {memberCount: 3, deviceCount: 3, slow: false},\n\t} {\n\t\tt.Run(fmt.Sprintf(\"testMemberStore seed: %d, memberCount: %d, deviceCount: %d\", seed, tc.memberCount, tc.deviceCount), func(t *testing.T) {\n\t\t\tif tc.slow {\n\t\t\t\t// TODO: re-enable this test\n\t\t\t\tt.Skip()\n\t\t\t\ttestutil.FilterSpeed(t, testutil.Slow)\n\t\t\t}\n\n\t\t\ttestMemberStore(t, tc.memberCount, tc.deviceCount)\n\t\t})\n\t}\n}\n\nfunc testMemberStore(t *testing.T, memberCount, deviceCount int) {\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\t// Creates N members with M devices each within the same group\n\tpeers, groupSK, cleanup := CreatePeersWithGroupTest(ctx, t, \"/tmp/member_test\", memberCount, deviceCount)\n\tdefer cleanup()\n\n\tdone := make(chan struct{})\n\n\tfor _, peer := range peers {\n\t\tgo waitForBertyEventType(ctx, t, peer.GC.MetadataStore(), protocoltypes.EventType_EventTypeGroupMemberDeviceAdded, len(peers), done)\n\t}\n\n\tfor i, peer := range peers {\n\t\tif _, err := peer.GC.MetadataStore().AddDeviceToGroup(ctx); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tif i == 0 {\n\t\t\tif _, err := peer.GC.MetadataStore().ClaimGroupOwnership(ctx, groupSK); err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t}\n\t}\n\n\t// Wait for all events to be received in all peers's member log (or timeout)\n\tfor i := range peers {\n\t\t<-done\n\t\tt.Logf(\"got all members for member %d\", i)\n\t}\n\n\t// Test if everything was replicated and indexed correctly\n\tfor i, peer := range peers {\n\t\tms := peer.GC.MetadataStore()\n\n\t\t// Test list functions (only length, checking all entries would be too long)\n\t\tmemberList := ms.ListMembers()\n\t\tif len(memberList) != memberCount {\n\t\t\tt.Fatalf(\"%d member(s) missing from peer %d member list (%d/%d)\", memberCount-len(memberList), i, len(memberList), memberCount)\n\t\t}\n\n\t\tdeviceList := ms.ListDevices()\n\t\tif len(deviceList) != memberCount*deviceCount {\n\t\t\tt.Fatalf(\"%d device(s) missing from peer %d device list (%d/%d)\", memberCount*deviceCount-len(deviceList), i, len(deviceList), memberCount*deviceCount)\n\t\t}\n\n\t\t// Test entries getter functions\n\t\tfor j, peerDev := range peers {\n\t\t\tif _, err := ms.GetDevicesForMember(peerDev.GC.MemberPubKey()); err != nil {\n\t\t\t\tt.Fatalf(\"member of peer %d is missing from peer %d member map: %v\", j, i, err)\n\t\t\t}\n\t\t\tif _, err := ms.GetMemberByDevice(peerDev.GC.MemberPubKey()); err != nil {\n\t\t\t\tt.Fatalf(\"device of peer %d is missing from peer %d device map: %v\", j, i, err)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc ipfsAPIUsingMockNet(ctx context.Context, t *testing.T) ipfsutil.ExtendedCoreAPI {\n\tipfsopts := &ipfsutil.TestingAPIOpts{\n\t\tLogger:    zap.NewNop(),\n\t\tMocknet:   mocknet.New(),\n\t\tDatastore: ds_sync.MutexWrap(datastore.NewMapDatastore()),\n\t}\n\n\tnode := ipfsutil.TestingCoreAPIUsingMockNet(ctx, t, ipfsopts)\n\n\treturn node.API()\n}\n\nfunc TestMetadataRendezvousPointLifecycle(t *testing.T) {\n\ttestutil.FilterSpeed(t, testutil.Fast)\n\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\t// Creates N members with M devices each within the same group\n\tpeers, _, cleanup := CreatePeersWithGroupTest(ctx, t, \"/tmp/member_test\", 1, 1)\n\tdefer cleanup()\n\n\tapi := ipfsAPIUsingMockNet(ctx, t)\n\n\townCG, err := peers[0].DB.openAccountGroup(ctx, nil, api)\n\tassert.NoError(t, err)\n\n\tmeta := ownCG.MetadataStore()\n\n\t_, accountMemberDevice, err := peers[0].SecretStore.GetGroupForAccount()\n\tassert.NoError(t, err)\n\taccPK, err := accountMemberDevice.Member().Raw()\n\tassert.NoError(t, err)\n\n\tenabled, shareableContact := meta.GetIncomingContactRequestsStatus()\n\tassert.False(t, enabled)\n\tassert.NotNil(t, shareableContact)\n\n\t// reset rdv seed\n\t_, err = meta.ContactRequestReferenceReset(ctx)\n\tassert.NoError(t, err)\n\n\t// get set shareable contact, not enabled\n\tenabled, shareableContact = meta.GetIncomingContactRequestsStatus()\n\tassert.False(t, enabled)\n\tassert.NotNil(t, shareableContact)\n\tassert.Equal(t, accPK, shareableContact.Pk)\n\tassert.Equal(t, 32, len(shareableContact.PublicRendezvousSeed))\n\n\t_, err = meta.ContactRequestEnable(ctx)\n\tassert.NoError(t, err)\n\n\tenabled, shareableContact = meta.GetIncomingContactRequestsStatus()\n\tassert.True(t, enabled)\n\tassert.NotNil(t, shareableContact)\n\n\t// Force reindex to check both enabled and seed\n\terr = meta.Load(ctx, -1)\n\tassert.NoError(t, err)\n\n\tenabled, shareableContact = meta.GetIncomingContactRequestsStatus()\n\tassert.True(t, enabled)\n\tassert.NotNil(t, shareableContact)\n\tassert.Equal(t, accPK, shareableContact.Pk)\n\tassert.Equal(t, 32, len(shareableContact.PublicRendezvousSeed))\n\n\t// Disable incoming contact requests\n\t_, err = meta.ContactRequestDisable(ctx)\n\tassert.NoError(t, err)\n\n\tenabled, shareableContact = meta.GetIncomingContactRequestsStatus()\n\tassert.False(t, enabled)\n\tassert.NotNil(t, shareableContact)\n\tassert.Equal(t, accPK, shareableContact.Pk)\n\tassert.Equal(t, 32, len(shareableContact.PublicRendezvousSeed))\n}\n\nfunc TestMetadataContactLifecycle(t *testing.T) {\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\tpeersCount := 4\n\n\t// Creates N members with M devices each within the same group\n\tpeers, _, cleanup := CreatePeersWithGroupTest(ctx, t, \"/tmp/member_test\", peersCount, 1)\n\tdefer cleanup()\n\n\tvar (\n\t\terr      error\n\t\tmeta     = make([]*MetadataStore, peersCount)\n\t\townCG    = make([]*GroupContext, peersCount)\n\t\tcontacts = make([]*protocoltypes.ShareableContact, peersCount)\n\t)\n\n\tapi := ipfsAPIUsingMockNet(ctx, t)\n\n\tfor i, p := range peers {\n\t\townCG[i], err = p.DB.openAccountGroup(ctx, nil, api)\n\t\trequire.NoError(t, err)\n\n\t\tmeta[i] = ownCG[i].MetadataStore()\n\t\t_, err = meta[i].ContactRequestReferenceReset(ctx)\n\t\trequire.NoError(t, err)\n\n\t\t_, contacts[i] = meta[i].GetIncomingContactRequestsStatus()\n\t\trequire.NotNil(t, contacts[i])\n\n\t\tcontacts[i].Metadata = []byte(fmt.Sprintf(\"own meta %d\", i))\n\t}\n\n\t_, randPK, err := crypto.GenerateEd25519Key(crand.Reader)\n\trequire.NoError(t, err)\n\n\t// no contacts\n\trequire.Equal(t, len(meta[0].Index().(*metadataStoreIndex).contacts), 0)\n\n\t_, err = meta[0].ContactRequestReferenceReset(ctx)\n\trequire.NoError(t, err)\n\n\tcontact2PK := contacts[1].Pk\n\tcontact2RDVS := contacts[1].PublicRendezvousSeed\n\n\t// Enqueuing outgoing\n\n\t_, err = meta[0].ContactRequestOutgoingEnqueue(ctx, contacts[0], contacts[0].Metadata)\n\trequire.Error(t, err)\n\n\t_, err = meta[0].ContactRequestOutgoingEnqueue(ctx, contacts[1], contacts[0].Metadata)\n\trequire.NoError(t, err)\n\n\t_, err = meta[0].ContactRequestOutgoingEnqueue(ctx, contacts[1], contacts[0].Metadata)\n\trequire.NoError(t, err)\n\n\trequire.Equal(t, len(meta[0].Index().(*metadataStoreIndex).contacts), 1)\n\trequire.NotNil(t, meta[0].Index().(*metadataStoreIndex).contacts[string(contact2PK)])\n\trequire.Equal(t, meta[0].Index().(*metadataStoreIndex).contacts[string(contact2PK)].state, protocoltypes.ContactState_ContactStateToRequest)\n\trequire.Equal(t, meta[0].Index().(*metadataStoreIndex).contacts[string(contact2PK)].contact.Pk, contact2PK)\n\trequire.Equal(t, meta[0].Index().(*metadataStoreIndex).contacts[string(contact2PK)].contact.PublicRendezvousSeed, contact2RDVS)\n\trequire.Equal(t, contacts[0].Metadata, meta[0].Index().(*metadataStoreIndex).contactRequestMetadata[string(contact2PK)])\n\n\tcontacts[1].PublicRendezvousSeed = nil\n\t_, err = meta[0].ContactRequestOutgoingEnqueue(ctx, contacts[1], contacts[0].Metadata)\n\trequire.Error(t, err)\n\n\tcontacts[1].PublicRendezvousSeed = []byte(\"too_short\")\n\t_, err = meta[0].ContactRequestOutgoingEnqueue(ctx, contacts[1], contacts[0].Metadata)\n\trequire.Error(t, err)\n\n\tcontacts[1].Pk = nil\n\tcontacts[1].PublicRendezvousSeed = contact2RDVS\n\t_, err = meta[0].ContactRequestOutgoingEnqueue(ctx, contacts[1], contacts[0].Metadata)\n\trequire.Error(t, err)\n\n\tcontacts[1].Pk = []byte(\"invalid\")\n\t_, err = meta[0].ContactRequestOutgoingEnqueue(ctx, contacts[1], contacts[0].Metadata)\n\trequire.Error(t, err)\n\n\trequire.Equal(t, 1, len(meta[0].Index().(*metadataStoreIndex).contacts))\n\trequire.NotNil(t, meta[0].Index().(*metadataStoreIndex).contacts[string(contact2PK)])\n\trequire.Equal(t, protocoltypes.ContactState_ContactStateToRequest, meta[0].Index().(*metadataStoreIndex).contacts[string(contact2PK)].state)\n\trequire.Equal(t, contact2PK, meta[0].Index().(*metadataStoreIndex).contacts[string(contact2PK)].contact.Pk)\n\trequire.Equal(t, contact2RDVS, meta[0].Index().(*metadataStoreIndex).contacts[string(contact2PK)].contact.PublicRendezvousSeed)\n\trequire.Equal(t, contacts[0].Metadata, meta[0].Index().(*metadataStoreIndex).contactRequestMetadata[string(contact2PK)])\n\n\tcontacts[1].Pk = contact2PK\n\tcontacts[1].PublicRendezvousSeed = contact2RDVS\n\tmeta[0].Index().(*metadataStoreIndex).contactRequestMetadata[string(contacts[1].Pk)] = contacts[0].Metadata\n\n\t// Marking as sent\n\n\t_, err = meta[0].ContactRequestOutgoingSent(ctx, nil)\n\trequire.Error(t, err)\n\n\t_, err = meta[0].ContactRequestOutgoingSent(ctx, randPK)\n\trequire.Error(t, err)\n\n\t_, err = meta[0].ContactRequestOutgoingSent(ctx, ownCG[0].MemberPubKey())\n\trequire.Error(t, err)\n\n\tmeta[0].Index().(*metadataStoreIndex).contacts[string(contacts[1].Pk)].state = protocoltypes.ContactState_ContactStateAdded\n\t_, err = meta[0].ContactRequestOutgoingSent(ctx, ownCG[1].MemberPubKey())\n\trequire.Error(t, err)\n\n\tmeta[0].Index().(*metadataStoreIndex).contacts[string(contacts[1].Pk)].state = protocoltypes.ContactState_ContactStateToRequest\n\t_, err = meta[0].ContactRequestOutgoingSent(ctx, ownCG[1].MemberPubKey())\n\trequire.NoError(t, err)\n\n\trequire.Equal(t, len(meta[0].Index().(*metadataStoreIndex).contacts), 1)\n\trequire.NotNil(t, meta[0].Index().(*metadataStoreIndex).contacts[string(contacts[1].Pk)])\n\trequire.Equal(t, protocoltypes.ContactState_ContactStateAdded, meta[0].Index().(*metadataStoreIndex).contacts[string(contacts[1].Pk)].state)\n\trequire.Equal(t, contacts[1].Pk, meta[0].Index().(*metadataStoreIndex).contacts[string(contacts[1].Pk)].contact.Pk)\n\trequire.Equal(t, contacts[1].PublicRendezvousSeed, meta[0].Index().(*metadataStoreIndex).contacts[string(contacts[1].Pk)].contact.PublicRendezvousSeed)\n\n\t// Marking as received\n\n\t_, err = meta[1].ContactRequestIncomingReceived(ctx, &protocoltypes.ShareableContact{})\n\trequire.Error(t, err)\n\n\t_, err = meta[1].ContactRequestIncomingReceived(ctx, &protocoltypes.ShareableContact{Pk: []byte(\"invalid\"), PublicRendezvousSeed: []byte(\"invalid\")})\n\trequire.Error(t, err)\n\n\t_, err = meta[1].ContactRequestIncomingReceived(ctx, &protocoltypes.ShareableContact{Pk: []byte(\"invalid\"), PublicRendezvousSeed: contacts[0].PublicRendezvousSeed})\n\trequire.Error(t, err)\n\n\t_, err = meta[1].ContactRequestIncomingReceived(ctx, &protocoltypes.ShareableContact{Pk: contacts[0].Pk, PublicRendezvousSeed: []byte(\"invalid\")})\n\trequire.Error(t, err)\n\n\t_, err = meta[1].ContactRequestIncomingReceived(ctx, contacts[1])\n\trequire.Error(t, err)\n\n\t_, err = meta[1].ContactRequestIncomingReceived(ctx, contacts[0])\n\trequire.NoError(t, err)\n\n\trequire.Equal(t, len(meta[1].Index().(*metadataStoreIndex).contacts), 1)\n\trequire.NotNil(t, meta[1].Index().(*metadataStoreIndex).contacts[string(contacts[0].Pk)])\n\trequire.Equal(t, protocoltypes.ContactState_ContactStateReceived, meta[1].Index().(*metadataStoreIndex).contacts[string(contacts[0].Pk)].state)\n\trequire.Equal(t, contacts[0].Pk, meta[1].Index().(*metadataStoreIndex).contacts[string(contacts[0].Pk)].contact.Pk)\n\trequire.Equal(t, contacts[0].PublicRendezvousSeed, meta[1].Index().(*metadataStoreIndex).contacts[string(contacts[0].Pk)].contact.PublicRendezvousSeed)\n\trequire.Equal(t, contacts[0].Metadata, meta[1].Index().(*metadataStoreIndex).contacts[string(contacts[0].Pk)].contact.Metadata)\n\n\t// Accepting received\n\n\t_, err = meta[1].ContactRequestIncomingAccept(ctx, nil)\n\trequire.Error(t, err)\n\n\t_, err = meta[1].ContactRequestIncomingAccept(ctx, randPK)\n\trequire.Error(t, err)\n\n\t_, err = meta[1].ContactRequestIncomingAccept(ctx, ownCG[1].MemberPubKey())\n\trequire.Error(t, err)\n\n\t_, err = meta[1].ContactRequestIncomingAccept(ctx, ownCG[0].MemberPubKey())\n\trequire.NoError(t, err)\n\n\trequire.Equal(t, len(meta[1].Index().(*metadataStoreIndex).contacts), 1)\n\trequire.NotNil(t, meta[1].Index().(*metadataStoreIndex).contacts[string(contacts[0].Pk)])\n\trequire.Equal(t, protocoltypes.ContactState_ContactStateAdded, meta[1].Index().(*metadataStoreIndex).contacts[string(contacts[0].Pk)].state)\n\trequire.Equal(t, contacts[0].Pk, meta[1].Index().(*metadataStoreIndex).contacts[string(contacts[0].Pk)].contact.Pk)\n\trequire.Equal(t, contacts[0].PublicRendezvousSeed, meta[1].Index().(*metadataStoreIndex).contacts[string(contacts[0].Pk)].contact.PublicRendezvousSeed)\n\n\t_, err = meta[1].ContactRequestIncomingReceived(ctx, contacts[0])\n\trequire.Error(t, err)\n\n\t// Auto accept when receiving an invitation from a contact you were waiting to send an invitation to\n\n\t_, err = meta[1].ContactRequestOutgoingEnqueue(ctx, contacts[3], contacts[1].Metadata)\n\trequire.NoError(t, err)\n\n\t_, err = meta[1].ContactRequestIncomingReceived(ctx, contacts[3])\n\trequire.NoError(t, err)\n\n\trequire.Equal(t, 2, len(meta[1].Index().(*metadataStoreIndex).contacts))\n\n\trequire.Equal(t, meta[1].Index().(*metadataStoreIndex).contacts[string(contacts[3].Pk)].state, protocoltypes.ContactState_ContactStateAdded)\n\n\t// Refuse contact\n\n\t_, err = meta[1].ContactRequestIncomingReceived(ctx, contacts[2])\n\trequire.NoError(t, err)\n\n\trequire.Equal(t, len(meta[1].Index().(*metadataStoreIndex).contacts), 3)\n\trequire.Equal(t, meta[1].Index().(*metadataStoreIndex).contacts[string(contacts[2].Pk)].state, protocoltypes.ContactState_ContactStateReceived)\n\n\t_, err = meta[1].ContactRequestIncomingDiscard(ctx, nil)\n\trequire.Error(t, err)\n\n\t_, err = meta[1].ContactRequestIncomingDiscard(ctx, randPK)\n\trequire.Error(t, err)\n\n\t_, err = meta[1].ContactRequestIncomingDiscard(ctx, ownCG[0].MemberPubKey())\n\trequire.Error(t, err)\n\n\t_, err = meta[1].ContactRequestIncomingDiscard(ctx, ownCG[1].MemberPubKey())\n\trequire.Error(t, err)\n\n\t_, err = meta[1].ContactRequestIncomingDiscard(ctx, ownCG[2].MemberPubKey())\n\trequire.NoError(t, err)\n\n\trequire.Equal(t, len(meta[1].Index().(*metadataStoreIndex).contacts), 3)\n\trequire.Equal(t, meta[1].Index().(*metadataStoreIndex).contacts[string(contacts[2].Pk)].state, protocoltypes.ContactState_ContactStateDiscarded)\n\n\t// Allow receiving requests again after discarded\n\n\t_, err = meta[1].ContactRequestIncomingReceived(ctx, contacts[2])\n\trequire.NoError(t, err)\n\n\trequire.Equal(t, len(meta[1].Index().(*metadataStoreIndex).contacts), 3)\n\trequire.Equal(t, meta[1].Index().(*metadataStoreIndex).contacts[string(contacts[2].Pk)].state, protocoltypes.ContactState_ContactStateReceived)\n\n\t_, err = meta[1].ContactRequestOutgoingEnqueue(ctx, contacts[2], contacts[1].Metadata)\n\trequire.NoError(t, err)\n\n\trequire.Equal(t, len(meta[1].Index().(*metadataStoreIndex).contacts), 3)\n\trequire.Equal(t, meta[1].Index().(*metadataStoreIndex).contacts[string(contacts[2].Pk)].state, protocoltypes.ContactState_ContactStateAdded)\n\n\t// Auto accept discarded requests\n\n\tmeta[1].Index().(*metadataStoreIndex).contacts[string(contacts[2].Pk)].state = protocoltypes.ContactState_ContactStateDiscarded\n\n\t_, err = meta[1].ContactRequestOutgoingEnqueue(ctx, contacts[2], contacts[1].Metadata)\n\trequire.NoError(t, err)\n\n\trequire.Equal(t, len(meta[1].Index().(*metadataStoreIndex).contacts), 3)\n\trequire.Equal(t, meta[1].Index().(*metadataStoreIndex).contacts[string(contacts[2].Pk)].state, protocoltypes.ContactState_ContactStateAdded)\n\n\t// Block contact\n\n\trequire.Equal(t, len(meta[2].Index().(*metadataStoreIndex).contacts), 0)\n\n\t_, err = meta[2].ContactBlock(ctx, ownCG[2].MemberPubKey())\n\trequire.Error(t, err)\n\trequire.Equal(t, len(meta[2].Index().(*metadataStoreIndex).contacts), 0)\n\n\t_, err = meta[2].ContactBlock(ctx, ownCG[0].MemberPubKey())\n\trequire.NoError(t, err)\n\trequire.Equal(t, len(meta[2].Index().(*metadataStoreIndex).contacts), 1)\n\trequire.Equal(t, meta[2].Index().(*metadataStoreIndex).contacts[string(contacts[0].Pk)].state, protocoltypes.ContactState_ContactStateBlocked)\n\n\t_, err = meta[2].ContactBlock(ctx, ownCG[0].MemberPubKey())\n\trequire.Error(t, err)\n\trequire.Equal(t, len(meta[2].Index().(*metadataStoreIndex).contacts), 1)\n\trequire.Equal(t, meta[2].Index().(*metadataStoreIndex).contacts[string(contacts[0].Pk)].state, protocoltypes.ContactState_ContactStateBlocked)\n\n\t// Unblock contact\n\n\t_, err = meta[2].ContactUnblock(ctx, ownCG[1].MemberPubKey())\n\trequire.Error(t, err)\n\trequire.Equal(t, len(meta[2].Index().(*metadataStoreIndex).contacts), 1)\n\trequire.Equal(t, meta[2].Index().(*metadataStoreIndex).contacts[string(contacts[0].Pk)].state, protocoltypes.ContactState_ContactStateBlocked)\n\n\t_, err = meta[2].ContactUnblock(ctx, ownCG[0].MemberPubKey())\n\trequire.NoError(t, err)\n\trequire.Equal(t, len(meta[2].Index().(*metadataStoreIndex).contacts), 1)\n\trequire.Equal(t, meta[2].Index().(*metadataStoreIndex).contacts[string(contacts[0].Pk)].state, protocoltypes.ContactState_ContactStateRemoved)\n\n\t_, err = meta[2].ContactUnblock(ctx, ownCG[0].MemberPubKey())\n\trequire.Error(t, err)\n\trequire.Equal(t, len(meta[2].Index().(*metadataStoreIndex).contacts), 1)\n\trequire.Equal(t, meta[2].Index().(*metadataStoreIndex).contacts[string(contacts[0].Pk)].state, protocoltypes.ContactState_ContactStateRemoved)\n}\n\nfunc TestMetadataAliasLifecycle(t *testing.T) {\n\ttestutil.FilterSpeed(t, testutil.Slow)\n\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\tpeersCount := 4\n\n\t// Creates N members with M devices each within the same group\n\tpeers, _, cleanup := CreatePeersWithGroupTest(ctx, t, \"/tmp/member_test\", peersCount, 1)\n\tdefer cleanup()\n\n\t// disclose\n\t_, err := peers[0].GC.MetadataStore().ContactSendAliasKey(ctx)\n\trequire.Error(t, err)\n\n\tg, err := peers[0].SecretStore.GetGroupForContact(peers[1].GC.MemberPubKey())\n\trequire.NoError(t, err)\n\n\tcg0, err := peers[0].DB.OpenGroup(ctx, g, nil)\n\trequire.NoError(t, err)\n\tdefer cg0.Close()\n\n\trequire.False(t, cg0.MetadataStore().Index().(*metadataStoreIndex).ownAliasKeySent)\n\n\t_, err = cg0.MetadataStore().AddDeviceToGroup(ctx)\n\trequire.NoError(t, err)\n\n\t_, err = cg0.MetadataStore().ContactSendAliasKey(ctx)\n\trequire.NoError(t, err)\n\n\trequire.Empty(t, cg0.MetadataStore().Index().(*metadataStoreIndex).otherAliasKey)\n\trequire.True(t, cg0.MetadataStore().Index().(*metadataStoreIndex).ownAliasKeySent)\n\n\tg, err = peers[1].SecretStore.GetGroupForContact(peers[0].GC.MemberPubKey())\n\trequire.NoError(t, err)\n\n\tcg1, err := peers[1].DB.OpenGroup(ctx, g, nil)\n\trequire.NoError(t, err)\n\tdefer cg1.Close()\n\n\t_ = cg1\n\n\t// TODO: receive key on cg1 from cg0\n\n\t// TODO: match received alias proof with previously disclosed key\n}\n\nfunc TestMetadataGroupsLifecycle(t *testing.T) {\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\t// Creates N members with M devices each within the same group\n\tpeers, _, cleanup := CreatePeersWithGroupTest(ctx, t, \"/tmp/member_test\", 1, 1)\n\tdefer cleanup()\n\n\tapi := ipfsAPIUsingMockNet(ctx, t)\n\n\townCG, err := peers[0].DB.openAccountGroup(ctx, nil, api)\n\tassert.NoError(t, err)\n\n\tg1, _, err := NewGroupMultiMember()\n\trequire.NoError(t, err)\n\n\tg2, _, err := NewGroupMultiMember()\n\trequire.NoError(t, err)\n\n\tg3, _, err := NewGroupMultiMember()\n\trequire.NoError(t, err)\n\n\tg1PK, err := g1.GetPubKey()\n\trequire.NoError(t, err)\n\n\tg3PK, err := g3.GetPubKey()\n\trequire.NoError(t, err)\n\n\t_, err = ownCG.MetadataStore().GroupJoin(ctx, g1)\n\trequire.NoError(t, err)\n\n\tgroups := ownCG.MetadataStore().ListMultiMemberGroups()\n\trequire.Len(t, groups, 1)\n\trequire.Equal(t, groups[0].PublicKey, g1.PublicKey)\n\trequire.Equal(t, groups[0].Secret, g1.Secret)\n\trequire.Equal(t, groups[0].SecretSig, g1.SecretSig)\n\trequire.Equal(t, groups[0].GroupType, g1.GroupType)\n\n\t_, err = ownCG.MetadataStore().GroupJoin(ctx, g2)\n\trequire.NoError(t, err)\n\n\tgroups = ownCG.MetadataStore().ListMultiMemberGroups()\n\trequire.Len(t, groups, 2)\n\tfirst, second := 0, 1\n\tif bytes.Compare(groups[1].PublicKey, g1.PublicKey) == 0 {\n\t\tfirst, second = 1, 0\n\t}\n\trequire.Equal(t, groups[first].PublicKey, g1.PublicKey)\n\trequire.Equal(t, groups[first].Secret, g1.Secret)\n\trequire.Equal(t, groups[first].SecretSig, g1.SecretSig)\n\trequire.Equal(t, groups[first].GroupType, g1.GroupType)\n\n\trequire.Equal(t, groups[second].PublicKey, g2.PublicKey)\n\trequire.Equal(t, groups[second].Secret, g2.Secret)\n\trequire.Equal(t, groups[second].SecretSig, g2.SecretSig)\n\trequire.Equal(t, groups[second].GroupType, g2.GroupType)\n\n\t_, err = ownCG.MetadataStore().GroupJoin(ctx, &protocoltypes.Group{\n\t\tPublicKey: []byte(\"invalid_pk\"),\n\t\tSecret:    g3.Secret,\n\t\tSecretSig: g3.SecretSig,\n\t\tGroupType: protocoltypes.GroupType_GroupTypeMultiMember,\n\t})\n\trequire.Error(t, err)\n\n\tgroups = ownCG.MetadataStore().ListMultiMemberGroups()\n\trequire.Len(t, groups, 2)\n\n\t_, err = ownCG.MetadataStore().GroupJoin(ctx, &protocoltypes.Group{\n\t\tPublicKey: g3.PublicKey,\n\t\tSecret:    nil,\n\t\tSecretSig: g3.SecretSig,\n\t\tGroupType: protocoltypes.GroupType_GroupTypeMultiMember,\n\t})\n\trequire.Error(t, err)\n\n\t_, err = ownCG.MetadataStore().GroupJoin(ctx, &protocoltypes.Group{\n\t\tPublicKey: g3.PublicKey,\n\t\tSecret:    g3.Secret,\n\t\tSecretSig: []byte(\"invalid_sig\"),\n\t\tGroupType: protocoltypes.GroupType_GroupTypeMultiMember,\n\t})\n\trequire.Error(t, err)\n\n\t_, err = ownCG.MetadataStore().GroupJoin(ctx, g1)\n\trequire.Error(t, err)\n\n\tgroups = ownCG.MetadataStore().ListMultiMemberGroups()\n\trequire.Len(t, groups, 2)\n\n\t_, err = ownCG.MetadataStore().GroupLeave(ctx, nil)\n\trequire.Error(t, err)\n\n\tgroups = ownCG.MetadataStore().ListMultiMemberGroups()\n\trequire.Len(t, groups, 2)\n\n\t_, err = ownCG.MetadataStore().GroupLeave(ctx, g1PK)\n\trequire.NoError(t, err)\n\n\tgroups = ownCG.MetadataStore().ListMultiMemberGroups()\n\trequire.Len(t, groups, 1)\n\n\t_, err = ownCG.MetadataStore().GroupLeave(ctx, g1PK)\n\trequire.Error(t, err)\n\n\tgroups = ownCG.MetadataStore().ListMultiMemberGroups()\n\trequire.Len(t, groups, 1)\n\n\t_, err = ownCG.MetadataStore().GroupLeave(ctx, g3PK)\n\trequire.Error(t, err)\n\n\tgroups = ownCG.MetadataStore().ListMultiMemberGroups()\n\trequire.Len(t, groups, 1)\n\trequire.Equal(t, groups[0].PublicKey, g2.PublicKey)\n\trequire.Equal(t, groups[0].Secret, g2.Secret)\n\trequire.Equal(t, groups[0].SecretSig, g2.SecretSig)\n\trequire.Equal(t, groups[0].GroupType, g2.GroupType)\n}\n\nfunc TestFlappyMultiDevices_Basic(t *testing.T) {\n\ttestutil.FilterStabilityAndSpeed(t, testutil.Flappy, testutil.Slow)\n\n\tmemberCount := 2\n\tdeviceCount := 3\n\ttotalDevices := memberCount * deviceCount\n\n\tctx, cancel := context.WithTimeout(context.Background(), time.Second*30)\n\tdefer cancel()\n\n\tpeers, _, cleanup := CreatePeersWithGroupTest(ctx, t, \"/tmp/multidevices_test\", memberCount, deviceCount)\n\tdefer cleanup()\n\n\tapi := ipfsAPIUsingMockNet(ctx, t)\n\t// make peer index\n\tpi := [][]int{}\n\tfor i := 0; i < memberCount; i++ {\n\t\tpi = append(pi, []int{})\n\t\tfor j := 0; j < deviceCount; j++ {\n\t\t\tpi[i] = append(pi[i], i*deviceCount+j)\n\t\t}\n\t}\n\n\tvar (\n\t\terr          error\n\t\tmeta         = make([]*MetadataStore, totalDevices)\n\t\townCG        = make([]*GroupContext, totalDevices)\n\t\tcontacts     = make([]*protocoltypes.ShareableContact, totalDevices)\n\t\tlistContacts map[string]*AccountContact\n\t\tgroups       []*protocoltypes.Group\n\t)\n\n\t// Activate account group + contact request\n\tfor i, p := range peers {\n\t\t// except for the latest peer devices\n\t\tif (i % deviceCount) == (deviceCount - 1) {\n\t\t\tcontinue\n\t\t}\n\t\townCG[i], err = p.DB.openAccountGroup(ctx, nil, api)\n\t\trequire.NoError(t, err)\n\n\t\tmeta[i] = ownCG[i].MetadataStore()\n\t\t_, err = meta[i].ContactRequestEnable(ctx)\n\t\tassert.NoError(t, err)\n\n\t\t_, err = meta[i].ContactRequestReferenceReset(ctx)\n\t\trequire.NoError(t, err)\n\n\t\t_, contacts[i] = meta[i].GetIncomingContactRequestsStatus()\n\t\trequire.NotNil(t, contacts[i])\n\n\t\tcontacts[i].Metadata = []byte(fmt.Sprintf(\"own meta %d\", i))\n\t}\n\n\tsyncChan := make(chan struct{})\n\tgo waitForBertyEventType(ctx, t, meta[pi[0][0]], protocoltypes.EventType_EventTypeAccountContactRequestOutgoingEnqueued, 1, syncChan)\n\tgo waitForBertyEventType(ctx, t, meta[pi[0][1]], protocoltypes.EventType_EventTypeAccountContactRequestOutgoingEnqueued, 1, syncChan)\n\tgo waitForBertyEventType(ctx, t, meta[pi[1][0]], protocoltypes.EventType_EventTypeAccountContactRequestIncomingReceived, 1, syncChan)\n\tgo waitForBertyEventType(ctx, t, meta[pi[1][1]], protocoltypes.EventType_EventTypeAccountContactRequestIncomingReceived, 1, syncChan)\n\n\t// Add peers to contact\n\t// Enqueuing outgoing\n\t_, err = meta[pi[0][0]].ContactRequestOutgoingEnqueue(ctx, contacts[pi[1][0]], contacts[pi[0][0]].Metadata)\n\trequire.NoError(t, err)\n\n\t// Marking as sent\n\t_, err = meta[pi[0][0]].ContactRequestOutgoingSent(ctx, ownCG[pi[1][0]].MemberPubKey())\n\trequire.NoError(t, err)\n\n\t// Marking as received\n\t_, err = meta[pi[1][0]].ContactRequestIncomingReceived(ctx, contacts[pi[0][0]])\n\trequire.NoError(t, err)\n\n\t// Accepting received\n\t_, err = meta[pi[1][0]].ContactRequestIncomingAccept(ctx, ownCG[pi[0][0]].MemberPubKey())\n\trequire.NoError(t, err)\n\n\tfor i := 0; i < 4; i++ {\n\t\tselect {\n\t\tcase <-syncChan:\n\t\tcase <-ctx.Done():\n\t\t\trequire.NoError(t, ctx.Err())\n\t\t}\n\t}\n\n\t// test if contact is established\n\tlistContacts = meta[pi[0][0]].ListContacts()\n\trequire.Equal(t, 1, len(listContacts))\n\trequire.NotNil(t, listContacts[string(contacts[pi[1][0]].Pk)])\n\tlistContacts = meta[pi[1][0]].ListContacts()\n\trequire.Equal(t, 1, len(listContacts))\n\trequire.NotNil(t, listContacts[string(contacts[pi[0][0]].Pk)])\n\n\t// test if 2nd devices have also the contact\n\tlistContacts = meta[pi[0][1]].ListContacts()\n\trequire.Equal(t, 1, len(listContacts))\n\trequire.NotNil(t, listContacts[string(contacts[pi[1][0]].Pk)])\n\tlistContacts = meta[pi[1][1]].ListContacts()\n\trequire.Equal(t, 1, len(listContacts))\n\trequire.NotNil(t, listContacts[string(contacts[pi[0][0]].Pk)])\n\n\t// Activate group for 2nd peer's 1st device\n\tgroups = meta[pi[1][0]].ListMultiMemberGroups()\n\trequire.Len(t, groups, 0)\n\tgo waitForBertyEventType(ctx, t, meta[pi[1][0]], protocoltypes.EventType_EventTypeAccountGroupJoined, 1, syncChan)\n\tgo waitForBertyEventType(ctx, t, meta[pi[1][1]], protocoltypes.EventType_EventTypeAccountGroupJoined, 1, syncChan)\n\t_, err = meta[pi[1][0]].GroupJoin(ctx, peers[pi[1][0]].GC.group)\n\trequire.NoError(t, err)\n\tfor i := 0; i < 2; i++ {\n\t\tselect {\n\t\tcase <-syncChan:\n\t\tcase <-ctx.Done():\n\t\t\trequire.NoError(t, ctx.Err())\n\t\t}\n\t}\n\tgroups = meta[pi[1][0]].ListMultiMemberGroups()\n\trequire.Len(t, groups, 1)\n\n\t// Test if other devices have the group too\n\tgroups = meta[pi[1][1]].ListMultiMemberGroups()\n\trequire.Len(t, groups, 1)\n\n\t// Check if a account group activate after the contact request is synchronized\n\t// Activate the 2nd peer's latest device account group\n\townCG[pi[1][2]], err = peers[pi[1][2]].DB.openAccountGroup(ctx, nil, api)\n\trequire.NoError(t, err)\n\tmeta[pi[1][2]] = ownCG[pi[1][2]].MetadataStore()\n\n\t// wait for replication db\n\t<-time.After(time.Second * 2)\n\n\tlistContacts = meta[pi[1][2]].ListContacts()\n\trequire.Equal(t, 1, len(listContacts))\n\trequire.NotNil(t, listContacts[string(contacts[0].Pk)])\n\n\t// Test for group\n\tgroups = meta[pi[1][2]].ListMultiMemberGroups()\n\trequire.Len(t, groups, 1)\n}\n"
  },
  {
    "path": "store_options.go",
    "content": "package weshnet\n\nimport (\n\t\"context\"\n\t\"encoding/hex\"\n\n\t\"github.com/libp2p/go-libp2p/p2p/host/eventbus\"\n\n\t\"berty.tech/go-ipfs-log/identityprovider\"\n\torbitdb \"berty.tech/go-orbit-db\"\n\t\"berty.tech/go-orbit-db/accesscontroller\"\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n)\n\nfunc DefaultOrbitDBOptions(g *protocoltypes.Group, options *orbitdb.CreateDBOptions, keystore *BertySignedKeyStore, storeType string, groupOpenMode GroupOpenMode) (*orbitdb.CreateDBOptions, error) {\n\tvar err error\n\n\tif options == nil {\n\t\toptions = &orbitdb.CreateDBOptions{}\n\t}\n\n\toptions = &orbitdb.CreateDBOptions{\n\t\tDirectory:               options.Directory,\n\t\tOverwrite:               options.Overwrite,\n\t\tLocalOnly:               options.LocalOnly,\n\t\tStoreType:               options.StoreType,\n\t\tAccessControllerAddress: options.AccessControllerAddress,\n\t\tAccessController:        options.AccessController,\n\t\tReplicate:               options.Replicate,\n\t\tCache:                   options.Cache,\n\t\tEventBus:                options.EventBus,\n\t\tLogger:                  options.Logger,\n\t}\n\n\tt := true\n\toptions.Create = &t\n\n\tif options.EventBus == nil {\n\t\toptions.EventBus = eventbus.NewBus()\n\t}\n\n\tif options.AccessController == nil {\n\t\toptions.AccessController, err = defaultACForGroup(g, storeType)\n\t\tif err != nil {\n\t\t\treturn nil, errcode.ErrCode_TODO.Wrap(err)\n\t\t}\n\t}\n\n\toptions.Keystore = keystore\n\tif groupOpenMode != GroupOpenModeReplicate {\n\t\toptions.Identity, err = defaultIdentityForGroup(context.TODO(), g, keystore)\n\t\tif err != nil {\n\t\t\treturn nil, errcode.ErrCode_TODO.Wrap(err)\n\t\t}\n\t} else {\n\t\toptions.Identity, err = readIdentityForGroup(g, keystore)\n\t\tif err != nil {\n\t\t\treturn nil, errcode.ErrCode_TODO.Wrap(err)\n\t\t}\n\t}\n\n\treturn options, nil\n}\n\nfunc defaultACForGroup(g *protocoltypes.Group, storeType string) (accesscontroller.ManifestParams, error) {\n\tgroupID := g.GroupIDAsString()\n\n\tsigPK, err := g.GetSigningPubKey()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_TODO.Wrap(err)\n\t}\n\n\tsigningKeyBytes, err := sigPK.Raw()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_TODO.Wrap(err)\n\t}\n\n\taccess := map[string][]string{\n\t\t\"write\":            {hex.EncodeToString(signingKeyBytes)},\n\t\tidentityGroupIDKey: {groupID},\n\t\tstoreTypeKey:       {storeType},\n\t}\n\n\taddress, err := simpleAccessControllerCID(access)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tparam := &accesscontroller.CreateAccessControllerOptions{\n\t\tAccess:       access,\n\t\tSkipManifest: true,\n\t\tType:         \"bertysimple\",\n\t\tAddress:      address,\n\t}\n\n\treturn param, nil\n}\n\nfunc defaultIdentityForGroup(ctx context.Context, g *protocoltypes.Group, ks *BertySignedKeyStore) (*identityprovider.Identity, error) {\n\tsigPK, err := g.GetSigningPubKey()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_TODO.Wrap(err)\n\t}\n\n\tsigningKeyBytes, err := sigPK.Raw()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_TODO.Wrap(err)\n\t}\n\n\tidentity, err := ks.getIdentityProvider().createIdentity(ctx, &identityprovider.CreateIdentityOptions{\n\t\tType:     identityType,\n\t\tKeystore: ks,\n\t\tID:       hex.EncodeToString(signingKeyBytes),\n\t})\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_TODO.Wrap(err)\n\t}\n\n\treturn identity, nil\n}\n\nfunc readIdentityForGroup(g *protocoltypes.Group, ks *BertySignedKeyStore) (*identityprovider.Identity, error) {\n\tsigPK, err := g.GetSigningPubKey()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_TODO.Wrap(err)\n\t}\n\n\tsigningKeyBytes, err := sigPK.Raw()\n\tif err != nil {\n\t\treturn nil, errcode.ErrCode_TODO.Wrap(err)\n\t}\n\n\treturn &identityprovider.Identity{\n\t\tID:         hex.EncodeToString(signingKeyBytes),\n\t\tPublicKey:  g.PublicKey,\n\t\tSignatures: &identityprovider.IdentitySignature{},\n\t\tType:       ks.getIdentityProvider().GetType(),\n\t\tProvider:   ks.getIdentityProvider(),\n\t}, nil\n}\n"
  },
  {
    "path": "store_utils.go",
    "content": "package weshnet\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\n\tipliface \"berty.tech/go-ipfs-log/iface\"\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n)\n\nfunc getEntriesInRange(entries []ipliface.IPFSLogEntry, since, until []byte) ([]ipliface.IPFSLogEntry, error) {\n\tvar (\n\t\tstartIndex, stopIndex int\n\t\tstartFound, stopFound bool\n\t)\n\n\tif since == nil {\n\t\tstartFound = true\n\t\tstartIndex = 0\n\t}\n\tif until == nil {\n\t\tstopFound = true\n\t\tstopIndex = len(entries) - 1\n\t}\n\n\tfor i, entry := range entries {\n\t\tif startFound && stopFound {\n\t\t\tbreak\n\t\t}\n\t\tif !startFound && bytes.Equal(entry.GetHash().Bytes(), since) {\n\t\t\tstartFound = true\n\t\t\tstartIndex = i\n\t\t}\n\t\tif !stopFound && bytes.Equal(entry.GetHash().Bytes(), until) {\n\t\t\tstopFound = true\n\t\t\tstopIndex = i\n\t\t}\n\t}\n\n\tif !startFound {\n\t\treturn nil, errcode.ErrCode_ErrInvalidRange.Wrap(errors.New(\"since ID not found\"))\n\t}\n\tif !stopFound {\n\t\treturn nil, errcode.ErrCode_ErrInvalidRange.Wrap(errors.New(\"until ID not found\"))\n\t}\n\tif startIndex > stopIndex && len(entries) > 0 {\n\t\treturn nil, errcode.ErrCode_ErrInvalidRange.Wrap(errors.New(\"since ID is after until ID\"))\n\t}\n\n\treturn entries[startIndex : stopIndex+1], nil\n}\n\nfunc iterateOverEntries(entries []ipliface.IPFSLogEntry, reverse bool, f func(ipliface.IPFSLogEntry)) {\n\tif reverse {\n\t\tfor i := len(entries) - 1; i > -1; i-- {\n\t\t\tf(entries[i])\n\t\t}\n\t} else {\n\t\tfor _, entry := range entries {\n\t\t\tf(entry)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "testing.go",
    "content": "package weshnet\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n\n\tgrpc_middleware \"github.com/grpc-ecosystem/go-grpc-middleware\"\n\tgrpc_zap \"github.com/grpc-ecosystem/go-grpc-middleware/logging/zap\"\n\tgrpc_ctxtags \"github.com/grpc-ecosystem/go-grpc-middleware/tags\"\n\tdatastore \"github.com/ipfs/go-datastore\"\n\tds_sync \"github.com/ipfs/go-datastore/sync\"\n\t\"github.com/libp2p/go-libp2p/core/crypto\"\n\t\"github.com/libp2p/go-libp2p/core/peer\"\n\tmocknet \"github.com/libp2p/go-libp2p/p2p/net/mock\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/zap\"\n\t\"google.golang.org/grpc\"\n\t\"google.golang.org/protobuf/proto\"\n\n\tencrepo \"berty.tech/go-ipfs-repo-encrypted\"\n\torbitdb \"berty.tech/go-orbit-db\"\n\t\"berty.tech/go-orbit-db/pubsub/pubsubraw\"\n\t\"berty.tech/weshnet/v2/internal/datastoreutil\"\n\t\"berty.tech/weshnet/v2/pkg/errcode\"\n\t\"berty.tech/weshnet/v2/pkg/ipfsutil\"\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n\t\"berty.tech/weshnet/v2/pkg/secretstore\"\n\t\"berty.tech/weshnet/v2/pkg/testutil\"\n\t\"berty.tech/weshnet/v2/pkg/tinder\"\n)\n\nconst InMemoryDir = \":memory:\"\n\nfunc NewTestOrbitDB(ctx context.Context, t *testing.T, logger *zap.Logger, node ipfsutil.CoreAPIMock, baseDS datastore.Batching) *WeshOrbitDB {\n\tt.Helper()\n\n\tapi := node.API()\n\tselfKey, err := api.Key().Self(ctx)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tbaseDS = datastoreutil.NewNamespacedDatastore(baseDS, datastore.NewKey(selfKey.ID().String()))\n\n\tsecretStore, err := secretstore.NewSecretStore(baseDS, nil)\n\trequire.NoError(t, err)\n\tt.Cleanup(func() {\n\t\t_ = secretStore.Close()\n\t})\n\n\tpubSub := pubsubraw.NewPubSub(node.PubSub(), selfKey.ID(), logger, nil)\n\n\todb, err := NewWeshOrbitDB(ctx, api, &NewOrbitDBOptions{\n\t\tDatastore:   baseDS,\n\t\tSecretStore: secretStore,\n\t\tNewOrbitDBOptions: orbitdb.NewOrbitDBOptions{\n\t\t\tLogger: logger,\n\t\t\tPubSub: pubSub,\n\t\t},\n\t})\n\trequire.NoError(t, err)\n\n\treturn odb\n}\n\ntype mockedPeer struct {\n\tCoreAPI     ipfsutil.CoreAPIMock\n\tDB          *WeshOrbitDB\n\tGC          *GroupContext\n\tSecretStore secretstore.SecretStore\n}\n\nfunc (m *mockedPeer) PeerInfo() peer.AddrInfo {\n\treturn m.CoreAPI.MockNode().Peerstore.PeerInfo(m.CoreAPI.MockNode().Identity)\n}\n\ntype TestingProtocol struct {\n\tOpts *Opts\n\n\tService Service\n\tClient  ServiceClient\n\n\tRootDatastore datastore.Batching\n\tIpfsCoreAPI   ipfsutil.ExtendedCoreAPI\n\tOrbitDB       *WeshOrbitDB\n\tSecretStore   secretstore.SecretStore\n}\n\ntype TestingOpts struct {\n\tLogger          *zap.Logger\n\tMocknet         mocknet.Mocknet\n\tDiscoveryServer *tinder.MockDriverServer\n\tSecretStore     secretstore.SecretStore\n\tCoreAPIMock     ipfsutil.CoreAPIMock\n\tOrbitDB         *WeshOrbitDB\n\tConnectFunc     ConnectTestingProtocolFunc\n}\n\nfunc NewTestingProtocol(ctx context.Context, t testing.TB, opts *TestingOpts, ds datastore.Batching) (*TestingProtocol, func()) {\n\tif opts == nil {\n\t\topts = &TestingOpts{}\n\t}\n\topts.applyDefaults(ctx, t)\n\n\tif ds == nil {\n\t\tds = ds_sync.MutexWrap(datastore.NewMapDatastore())\n\t}\n\n\tipfsopts := &ipfsutil.TestingAPIOpts{\n\t\tLogger:          opts.Logger,\n\t\tMocknet:         opts.Mocknet,\n\t\tDiscoveryServer: opts.DiscoveryServer,\n\t\tDatastore:       ds,\n\t}\n\n\tnode := opts.CoreAPIMock\n\tif node == nil {\n\t\tnode = ipfsutil.TestingCoreAPIUsingMockNet(ctx, t, ipfsopts)\n\t}\n\n\tsecretStore := opts.SecretStore\n\tif secretStore == nil {\n\t\tvar err error\n\t\tsecretStore, err = secretstore.NewInMemSecretStore(&secretstore.NewSecretStoreOptions{})\n\t\trequire.NoError(t, err)\n\t}\n\n\todb := opts.OrbitDB\n\tif odb == nil {\n\t\tvar err error\n\n\t\tpubSub := pubsubraw.NewPubSub(node.PubSub(), node.MockNode().PeerHost.ID(), opts.Logger, nil)\n\n\t\todb, err = NewWeshOrbitDB(ctx, node.API(), &NewOrbitDBOptions{\n\t\t\tNewOrbitDBOptions: orbitdb.NewOrbitDBOptions{\n\t\t\t\tPubSub: pubSub,\n\t\t\t\tLogger: opts.Logger,\n\t\t\t},\n\t\t\tDatastore:   ds,\n\t\t\tSecretStore: secretStore,\n\t\t})\n\t\trequire.NoError(t, err)\n\t}\n\n\tserviceOpts := Opts{\n\t\tHost:          node.MockNode().PeerHost,\n\t\tPubSub:        node.PubSub(),\n\t\tLogger:        opts.Logger,\n\t\tRootDatastore: ds,\n\t\tIpfsCoreAPI:   node.API(),\n\t\tOrbitDB:       odb,\n\t\tTinderService: node.Tinder(),\n\t\tSecretStore:   secretStore,\n\t}\n\n\tservice, cleanupService := TestingService(ctx, t, serviceOpts)\n\n\t// setup client\n\tgrpcLogger := opts.Logger.Named(\"grpc\")\n\tzapOpts := []grpc_zap.Option{}\n\n\tserverOpts := []grpc.ServerOption{\n\t\tgrpc_middleware.WithUnaryServerChain(\n\t\t\tgrpc_ctxtags.UnaryServerInterceptor(grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.CodeGenRequestFieldExtractor)),\n\t\t\tgrpc_zap.UnaryServerInterceptor(grpcLogger, zapOpts...),\n\t\t),\n\t\tgrpc_middleware.WithStreamServerChain(\n\t\t\tgrpc_ctxtags.StreamServerInterceptor(grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.CodeGenRequestFieldExtractor)),\n\t\t\tgrpc_zap.StreamServerInterceptor(grpcLogger, zapOpts...),\n\t\t),\n\t}\n\n\tclientOpts := []grpc.DialOption{\n\t\tgrpc.WithChainUnaryInterceptor(),\n\t\tgrpc.WithChainStreamInterceptor(),\n\t}\n\n\tserver := grpc.NewServer(serverOpts...)\n\tclient, cleanupClient := TestingClientFromServer(ctx, t, server, service, clientOpts...)\n\n\ttp := &TestingProtocol{\n\t\tOpts:    &serviceOpts,\n\t\tClient:  client,\n\t\tService: service,\n\n\t\tRootDatastore: ds,\n\t\tIpfsCoreAPI:   node.API(),\n\t\tOrbitDB:       odb,\n\t\tSecretStore:   secretStore,\n\t}\n\tcleanup := func() {\n\t\tserver.Stop()\n\t\tcleanupClient()\n\t\tcleanupService()\n\t}\n\treturn tp, cleanup\n}\n\nfunc (opts *TestingOpts) applyDefaults(ctx context.Context, t testing.TB) {\n\tif opts.Logger == nil {\n\t\topts.Logger = zap.NewNop()\n\t}\n\n\tif opts.Mocknet == nil {\n\t\topts.Mocknet = mocknet.New()\n\t\tt.Cleanup(func() { opts.Mocknet.Close() })\n\t}\n\n\tif opts.ConnectFunc == nil {\n\t\topts.ConnectFunc = ConnectAll\n\t}\n}\n\nfunc NewTestingProtocolWithMockedPeers(ctx context.Context, t testing.TB, opts *TestingOpts, ds datastore.Batching, amount int) ([]*TestingProtocol, func()) {\n\tt.Helper()\n\topts.applyDefaults(ctx, t)\n\tlogger := opts.Logger\n\n\tif ds == nil {\n\t\tds = ds_sync.MutexWrap(datastore.NewMapDatastore())\n\t}\n\n\tif opts.DiscoveryServer == nil {\n\t\topts.DiscoveryServer = tinder.NewMockDriverServer()\n\t}\n\n\tcls := make([]func(), amount)\n\ttps := make([]*TestingProtocol, amount)\n\tfor i := range tps {\n\t\tsvcName := fmt.Sprintf(\"mock%d\", i)\n\t\topts.Logger = logger.Named(svcName)\n\t\tds := datastoreutil.NewNamespacedDatastore(ds, datastore.NewKey(fmt.Sprintf(\"%d\", i)))\n\n\t\ttps[i], cls[i] = NewTestingProtocol(ctx, t, opts, ds)\n\t}\n\n\topts.ConnectFunc(t, opts.Mocknet)\n\n\tcleanup := func() {\n\t\tfor i := range cls {\n\t\t\tcls[i]()\n\t\t}\n\t}\n\treturn tps, cleanup\n}\n\n// TestingService returns a configured Client struct with in-memory contexts.\nfunc TestingService(ctx context.Context, t testing.TB, opts Opts) (Service, func()) {\n\tt.Helper()\n\n\tif opts.Logger == nil {\n\t\topts.Logger = zap.NewNop()\n\t}\n\n\tif opts.IpfsCoreAPI == nil {\n\t\tvar mn ipfsutil.CoreAPIMock\n\t\tmn = ipfsutil.TestingCoreAPI(ctx, t)\n\t\topts.IpfsCoreAPI = mn.API()\n\t}\n\n\tservice, err := NewService(opts)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to initialize client: %v\", err)\n\t}\n\n\tcleanup := func() {\n\t\tservice.Close()\n\t}\n\n\treturn service, cleanup\n}\n\nfunc TestingClientFromServer(ctx context.Context, t testing.TB, s *grpc.Server, svc Service, dialOpts ...grpc.DialOption) (client ServiceClient, cleanup func()) {\n\tt.Helper()\n\n\tvar err error\n\n\tclient, err = NewClientFromService(ctx, s, svc, dialOpts...)\n\trequire.NoError(t, err)\n\tcleanup = func() {\n\t\tclient.Close()\n\t}\n\n\treturn\n}\n\nfunc TestingClient(ctx context.Context, t testing.TB, svc Service, clientOpts []grpc.DialOption, serverOpts []grpc.ServerOption) (client ServiceClient, cleanup func()) {\n\tt.Helper()\n\n\tvar err error\n\n\tsrv := grpc.NewServer(serverOpts...)\n\n\tclient, err = NewClientFromService(ctx, srv, svc, clientOpts...)\n\trequire.NoError(t, err)\n\n\tcleanup = func() {\n\t\tsrv.Stop()\n\t\tclient.Close()\n\t}\n\n\treturn\n}\n\n// Connect Peers Helper\ntype ConnectTestingProtocolFunc func(testing.TB, mocknet.Mocknet)\n\n// ConnectAll peers between themselves\nfunc ConnectAll(t testing.TB, m mocknet.Mocknet) {\n\tt.Helper()\n\n\terr := m.LinkAll()\n\trequire.NoError(t, err)\n\n\terr = m.ConnectAllButSelf()\n\trequire.NoError(t, err)\n}\n\n// ConnectInLine, connect peers one by one in order to make a straight line:\n// ┌───┐    ┌───┐    ┌───┐         ┌───┐\n// │ 1 │───▶│ 2 │───▶│ 3 │─ ─ ─ ─ ▶│ x │\n// └───┘    └───┘    └───┘         └───┘\n\nfunc ConnectInLine(t testing.TB, m mocknet.Mocknet) {\n\tt.Helper()\n\n\tpeers := m.Peers()\n\n\tfor i := 0; i < len(peers)-1; i++ {\n\t\t_, err := m.LinkPeers(peers[i], peers[i+1])\n\t\trequire.NoError(t, err)\n\n\t\t_, err = m.ConnectPeers(peers[i], peers[i+1])\n\t\trequire.NoError(t, err)\n\t}\n}\n\nfunc CreatePeersWithGroupTest(ctx context.Context, t testing.TB, pathBase string, memberCount int, deviceCount int) ([]*mockedPeer, crypto.PrivKey, func()) {\n\tt.Helper()\n\n\tlogger, cleanupLogger := testutil.Logger(t)\n\n\tvar secretStore secretstore.SecretStore\n\n\tmockedPeers := make([]*mockedPeer, memberCount*deviceCount)\n\n\tgroup, groupPrivateKey, err := NewGroupMultiMember()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tmn := mocknet.New()\n\tt.Cleanup(func() { mn.Close() })\n\n\tipfsopts := ipfsutil.TestingAPIOpts{\n\t\tLogger:          logger,\n\t\tMocknet:         mn,\n\t\tDiscoveryServer: tinder.NewMockDriverServer(),\n\t}\n\tdeviceIndex := 0\n\n\tcls := make([]func(), memberCount)\n\tfor i := 0; i < memberCount; i++ {\n\t\tfor j := 0; j < deviceCount; j++ {\n\t\t\tca := ipfsutil.TestingCoreAPIUsingMockNet(ctx, t, &ipfsopts)\n\n\t\t\tif j == 0 {\n\t\t\t\tsecretStore, err = secretstore.NewInMemSecretStore(nil)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t} else {\n\t\t\t\tprivateKeyBytes, proofPrivateKeyBytes, err := secretStore.ExportAccountKeysForBackup()\n\t\t\t\trequire.NoError(t, err, \"ExportAccountKeysForBackup error\")\n\n\t\t\t\tsecretStore, err = secretstore.NewInMemSecretStore(nil)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.NoError(t, secretStore.ImportAccountKeys(privateKeyBytes, proofPrivateKeyBytes))\n\t\t\t}\n\n\t\t\tdb, err := NewWeshOrbitDB(ctx, ca.API(), &NewOrbitDBOptions{\n\t\t\t\tNewOrbitDBOptions: orbitdb.NewOrbitDBOptions{\n\t\t\t\t\tLogger: logger,\n\t\t\t\t},\n\t\t\t\tSecretStore: secretStore,\n\t\t\t})\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\n\t\t\tgc, err := db.OpenGroup(ctx, group, nil)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"err: creating new group context, %v\", err)\n\t\t\t}\n\n\t\t\tmp := &mockedPeer{\n\t\t\t\tCoreAPI:     ca,\n\t\t\t\tDB:          db,\n\t\t\t\tGC:          gc,\n\t\t\t\tSecretStore: secretStore,\n\t\t\t}\n\n\t\t\t// setup cleanup\n\t\t\tcls[i] = func() {\n\t\t\t\tif ms := mp.GC.MetadataStore(); ms != nil {\n\t\t\t\t\terr := ms.Drop()\n\t\t\t\t\tassert.NoError(t, err)\n\t\t\t\t}\n\n\t\t\t\tgc.Close()\n\n\t\t\t\tif db := mp.DB; db != nil {\n\t\t\t\t\tassert.NoError(t, err)\n\n\t\t\t\t\terr = db.Close()\n\t\t\t\t\tassert.NoError(t, err)\n\t\t\t\t}\n\n\t\t\t\t_ = secretStore.Close()\n\t\t\t}\n\n\t\t\tmockedPeers[deviceIndex] = mp\n\t\t\tdeviceIndex++\n\t\t}\n\t}\n\n\tconnectPeers(ctx, t, ipfsopts.Mocknet)\n\n\treturn mockedPeers, groupPrivateKey, func() {\n\t\tfor _, cleanup := range cls {\n\t\t\tcleanup()\n\t\t}\n\n\t\tcleanupLogger()\n\t}\n}\n\nfunc connectPeers(ctx context.Context, t testing.TB, mn mocknet.Mocknet) {\n\tt.Helper()\n\n\terr := mn.LinkAll()\n\trequire.NoError(t, err)\n\n\terr = mn.ConnectAllButSelf()\n\trequire.NoError(t, err)\n}\n\nfunc dropPeers(t *testing.T, mockedPeers []*mockedPeer) {\n\tt.Helper()\n\n\tfor _, m := range mockedPeers {\n\t\tif ms := m.GC.MetadataStore(); ms != nil {\n\t\t\tif err := ms.Drop(); err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t}\n\n\t\tif db := m.DB; db != nil {\n\t\t\tif err := db.Close(); err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t}\n\n\t\tif ca := m.CoreAPI; ca != nil {\n\t\t\tif err := ca.MockNode().Close(); err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t}\n\t}\n}\n\ntype ServiceMethods interface {\n\tGetContextGroupForID(id []byte) (*GroupContext, error)\n}\n\nfunc GetRootDatastoreForPath(dir string, key []byte, salt []byte, logger *zap.Logger) (datastore.Batching, error) {\n\tinMemory := dir == InMemoryDir\n\n\tvar ds datastore.Batching\n\tif inMemory {\n\t\tds = datastore.NewMapDatastore()\n\t} else {\n\t\terr := os.MkdirAll(dir, os.ModePerm)\n\t\tif err != nil {\n\t\t\treturn nil, errcode.ErrCode_TODO.Wrap(err)\n\t\t}\n\n\t\tdbPath := filepath.Join(dir, \"datastore.sqlite\")\n\t\tsqldsOpts := encrepo.SQLCipherDatastoreOptions{JournalMode: \"WAL\", PlaintextHeader: len(salt) != 0, Salt: salt}\n\t\tds, err = encrepo.NewSQLCipherDatastore(\"sqlite3\", dbPath, \"blocks\", key, sqldsOpts)\n\t\tif err != nil {\n\t\t\treturn nil, errcode.ErrCode_TODO.Wrap(err)\n\t\t}\n\t}\n\n\tds = ds_sync.MutexWrap(ds)\n\n\treturn ds, nil\n}\n\nfunc CreateMultiMemberGroupInstance(ctx context.Context, t *testing.T, tps ...*TestingProtocol) *protocoltypes.Group {\n\ttestutil.LogTree(t, \"Create and Join MultiMember Group\", 0, true)\n\tstart := time.Now()\n\n\tntps := len(tps)\n\n\t// Create group\n\tgroup, _, err := NewGroupMultiMember()\n\trequire.NoError(t, err)\n\n\t// Get Instance Configurations\n\t{\n\t\ttestutil.LogTree(t, \"Get Instance Configuration\", 1, true)\n\t\tstart := time.Now()\n\n\t\t// check if everything is ready\n\t\tfor _, pt := range tps {\n\t\t\t_, err := pt.Client.ServiceGetConfiguration(ctx, &protocoltypes.ServiceGetConfiguration_Request{})\n\t\t\trequire.NoError(t, err)\n\t\t}\n\n\t\ttestutil.LogTree(t, \"duration: %s\", 1, false, time.Since(start))\n\t}\n\n\t// Join Group\n\t{\n\t\ttestutil.LogTree(t, \"Join Group\", 1, true)\n\t\tstart := time.Now()\n\n\t\tfor _, pt := range tps {\n\t\t\treq := protocoltypes.MultiMemberGroupJoin_Request{\n\t\t\t\tGroup: group,\n\t\t\t}\n\n\t\t\t// pt join group\n\t\t\t_, err = pt.Client.MultiMemberGroupJoin(ctx, &req)\n\t\t\trequire.NoError(t, err)\n\t\t}\n\n\t\ttestutil.LogTree(t, \"duration: %s\", 1, false, time.Since(start))\n\t}\n\n\t// Get Member/Device PKs\n\tmemberPKs := make([][]byte, ntps)\n\tdevicePKs := make([][]byte, ntps)\n\t{\n\t\ttestutil.LogTree(t, \"Get Member/Device PKs\", 1, true)\n\t\tstart := time.Now()\n\n\t\tfor i, pt := range tps {\n\t\t\tres, err := pt.Client.GroupInfo(ctx, &protocoltypes.GroupInfo_Request{\n\t\t\t\tGroupPk: group.PublicKey,\n\t\t\t})\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, group.PublicKey, res.Group.PublicKey)\n\n\t\t\tmemberPKs[i] = res.MemberPk\n\t\t\tdevicePKs[i] = res.DevicePk\n\t\t}\n\n\t\ttestutil.LogTree(t, \"duration: %s\", 1, false, time.Since(start))\n\t}\n\n\t// Activate Group\n\t{\n\t\ttestutil.LogTree(t, \"Activate Group\", 1, true)\n\t\tstart := time.Now()\n\n\t\tfor i, pt := range tps {\n\t\t\t_, err := pt.Client.ActivateGroup(ctx, &protocoltypes.ActivateGroup_Request{\n\t\t\t\tGroupPk: group.PublicKey,\n\t\t\t})\n\n\t\t\tassert.NoError(t, err, fmt.Sprintf(\"error for client %d\", i))\n\t\t}\n\n\t\ttestutil.LogTree(t, \"duration: %s\", 1, false, time.Since(start))\n\t}\n\n\t// Exchange Secrets\n\t{\n\t\ttestutil.LogTree(t, \"Exchange Secrets\", 1, true)\n\t\tstart := time.Now()\n\n\t\twg := sync.WaitGroup{}\n\t\tsecretsReceivedLock := sync.Mutex{}\n\t\tsecretsReceived := make([]map[string]struct{}, ntps)\n\t\twg.Add(ntps)\n\n\t\tnSuccess := int64(0)\n\t\tfor i := range tps {\n\t\t\tgo func(i int) {\n\t\t\t\ttp := tps[i]\n\n\t\t\t\tdefer wg.Done()\n\n\t\t\t\tsecretsReceived[i] = map[string]struct{}{}\n\n\t\t\t\tctx, cancel := context.WithCancel(ctx)\n\t\t\t\tdefer cancel()\n\n\t\t\t\tsub, inErr := tp.Client.GroupMetadataList(ctx, &protocoltypes.GroupMetadataList_Request{\n\t\t\t\t\tGroupPk: group.PublicKey,\n\t\t\t\t})\n\t\t\t\tif inErr != nil {\n\t\t\t\t\tassert.NoError(t, err, fmt.Sprintf(\"error for client %d\", i))\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tfor {\n\t\t\t\t\tevt, inErr := sub.Recv()\n\t\t\t\t\tif inErr != nil {\n\t\t\t\t\t\tif inErr != io.EOF {\n\t\t\t\t\t\t\tassert.NoError(t, err, fmt.Sprintf(\"error for client %d\", i))\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\n\t\t\t\t\tif source, err := isEventAddSecretTargetedToMember(memberPKs[i], evt); err != nil {\n\t\t\t\t\t\ttps[i].Opts.Logger.Error(\"err:\", zap.Error(inErr))\n\t\t\t\t\t\tassert.NoError(t, err, fmt.Sprintf(\"error for client %d\", i))\n\n\t\t\t\t\t\tbreak\n\t\t\t\t\t} else if source != nil {\n\t\t\t\t\t\tsecretsReceivedLock.Lock()\n\t\t\t\t\t\tsecretsReceived[i][string(source)] = struct{}{}\n\t\t\t\t\t\tdone := len(secretsReceived[i]) == ntps\n\t\t\t\t\t\tsecretsReceivedLock.Unlock()\n\n\t\t\t\t\t\tif done {\n\t\t\t\t\t\t\tn := atomic.AddInt64(&nSuccess, 1)\n\n\t\t\t\t\t\t\tgot := fmt.Sprintf(\"%d/%d\", n, ntps)\n\t\t\t\t\t\t\ttps[i].Opts.Logger.Debug(\"received all secrets\", zap.String(\"ok\", got))\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}(i)\n\t\t}\n\n\t\twg.Wait()\n\n\t\tsecretsReceivedLock.Lock()\n\t\tok := true\n\t\tfor i := range secretsReceived {\n\t\t\tif !assert.Equal(t, ntps, len(secretsReceived[i]), fmt.Sprintf(\"mismatch for client %d\", i)) {\n\t\t\t\tok = false\n\t\t\t}\n\t\t}\n\t\trequire.True(t, ok)\n\t\tsecretsReceivedLock.Unlock()\n\n\t\ttestutil.LogTree(t, \"duration: %s\", 1, false, time.Since(start))\n\t}\n\n\ttestutil.LogTree(t, \"duration: %s\", 0, false, time.Since(start))\n\n\treturn group\n}\n\nfunc isEventAddSecretTargetedToMember(ownRawPK []byte, evt *protocoltypes.GroupMetadataEvent) ([]byte, error) {\n\t// Only count EventTypeGroupDeviceChainKeyAdded events\n\tif evt.Metadata.EventType != protocoltypes.EventType_EventTypeGroupDeviceChainKeyAdded {\n\t\treturn nil, nil\n\t}\n\n\tsec := &protocoltypes.GroupDeviceChainKeyAdded{}\n\terr := proto.Unmarshal(evt.Event, sec)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Filter out events targeted at other members\n\tif !bytes.Equal(ownRawPK, sec.DestMemberPk) {\n\t\treturn nil, nil\n\t}\n\n\treturn sec.DevicePk, nil\n}\n"
  },
  {
    "path": "testing_test.go",
    "content": "package weshnet\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"berty.tech/weshnet/v2/pkg/protocoltypes\"\n)\n\nfunc TestClient_impl(t *testing.T) {\n\tvar _ Service = (*service)(nil)\n\tvar _ protocoltypes.ProtocolServiceServer = (*service)(nil)\n}\n\nfunc TestEmptyArgs(t *testing.T) {\n\t// disable resources manager for test\n\tos.Setenv(\"LIBP2P_RCMGR\", \"false\")\n\n\t// initialize new client\n\tclient, err := NewService(Opts{})\n\trequire.NoError(t, err)\n\terr = client.Close()\n\trequire.NoError(t, err)\n\tclient.Close()\n}\n\nfunc TestTestingProtocol(t *testing.T) {\n\tctx, cancel := context.WithCancel(context.Background())\n\topts := TestingOpts{}\n\ttp, cleanup := NewTestingProtocol(ctx, t, &opts, nil)\n\tassert.NotNil(t, tp)\n\tcleanup()\n\tcancel()\n}\n\nfunc TestTestingProtocolWithMockedPeers(t *testing.T) {\n\tfor amount := 0; amount < 5; amount++ {\n\t\tt.Run(fmt.Sprintf(\"%d-peers\", amount), func(t *testing.T) {\n\t\t\tctx, cancel := context.WithCancel(context.Background())\n\t\t\topts := TestingOpts{}\n\t\t\ttp, cleanup := NewTestingProtocolWithMockedPeers(ctx, t, &opts, nil, amount)\n\t\t\tassert.NotNil(t, tp)\n\t\t\tcleanup()\n\t\t\tcancel()\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "tinder_swiper.go",
    "content": "package weshnet\n\nimport (\n\t\"context\"\n\t\"encoding/base64\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\tmrand \"math/rand\"\n\t\"sync\"\n\t\"time\"\n\n\tpubsub \"github.com/libp2p/go-libp2p-pubsub\"\n\t\"github.com/libp2p/go-libp2p/core/peer\"\n\tbackoff \"github.com/libp2p/go-libp2p/p2p/discovery/backoff\"\n\t\"go.uber.org/zap\"\n\t\"moul.io/srand\"\n\n\t\"berty.tech/weshnet/v2/pkg/logutil\"\n\t\"berty.tech/weshnet/v2/pkg/rendezvous\"\n\ttinder \"berty.tech/weshnet/v2/pkg/tinder\"\n\t\"berty.tech/weshnet/v2/pkg/tyber\"\n)\n\ntype swiperRequest struct {\n\tbstrat    backoff.BackoffStrategy\n\twgRefresh *sync.WaitGroup\n\tctx       context.Context\n\tout       chan<- peer.AddrInfo\n\trdvTopic  string\n}\n\ntype Swiper struct {\n\ttopics map[string]*pubsub.Topic\n\n\tbackoffFactory backoff.BackoffFactory\n\n\tinprogressLookup map[string]*swiperRequest\n\tmuRequest        sync.Mutex\n\n\trp *rendezvous.RotationInterval\n\n\tlogger *zap.Logger\n\ttinder *tinder.Service\n}\n\nfunc NewSwiper(logger *zap.Logger, tinder *tinder.Service, rp *rendezvous.RotationInterval) *Swiper {\n\t// we need to use math/rand here, but it is seeded from crypto/rand\n\tsrand := mrand.New(mrand.NewSource(srand.MustSecure())) // nolint:gosec\n\tbackoffstrat := backoff.NewExponentialBackoff(time.Second, time.Minute*10,\n\t\tbackoff.FullJitter,\n\t\ttime.Second, 30.0, 0, srand)\n\n\treturn &Swiper{\n\t\tbackoffFactory:   backoffstrat,\n\t\tlogger:           logger.Named(\"swiper\"),\n\t\ttopics:           make(map[string]*pubsub.Topic),\n\t\tinprogressLookup: make(map[string]*swiperRequest),\n\t\trp:               rp,\n\t\ttinder:           tinder,\n\t}\n}\n\nfunc (s *Swiper) RefreshContactRequest(ctx context.Context, topic []byte) (addrs []peer.AddrInfo, err error) {\n\tctx, _, endSection := tyber.Section(ctx, s.logger, \"swiper starting refresh: \"+hex.EncodeToString(topic))\n\tdefer func() {\n\t\tendSection(err, \"\")\n\t}()\n\n\t// canceling find peers\n\ts.muRequest.Lock()\n\treq, ok := s.inprogressLookup[base64.StdEncoding.EncodeToString(topic)]\n\tif !ok {\n\t\terr = fmt.Errorf(\"unknown topic\")\n\t\ts.muRequest.Unlock()\n\t\treturn addrs, err\n\t}\n\n\t// add a refresh job process\n\treq.wgRefresh.Add(1)\n\tdefer req.wgRefresh.Done()\n\n\ts.muRequest.Unlock()\n\n\tctx, cancel := context.WithCancel(ctx)\n\tdefer cancel()\n\n\tgo func() {\n\t\t// if rotation topic is outdated, cancel research\n\t\t<-req.ctx.Done()\n\t\tcancel()\n\t}()\n\n\t// force find peers re check topic\n\tcpeer := s.tinder.FindPeers(req.ctx, req.rdvTopic)\n\tselect {\n\tcase p := <-cpeer:\n\t\treq.out <- p\n\t\treturn []peer.AddrInfo{p}, nil\n\tcase <-ctx.Done():\n\t\treturn nil, ctx.Err()\n\t}\n}\n\n// WatchTopic looks for peers providing a resource.\n// 'done' is used to alert parent when everything is done, to avoid data races.\nfunc (s *Swiper) WatchTopic(ctx context.Context, topic, seed []byte) <-chan peer.AddrInfo {\n\tctx, _, endSection := tyber.Section(ctx, s.logger, \"swiper looking for peers: \"+hex.EncodeToString(topic))\n\n\ts.muRequest.Lock()\n\tdefer s.muRequest.Unlock()\n\n\ts.logger.Debug(\"start watch for peer with\",\n\t\tlogutil.PrivateString(\"topic\", base64.StdEncoding.EncodeToString(topic)),\n\t\tzap.String(\"seed\", string(seed)))\n\n\tvar point *rendezvous.Point\n\n\tcpeers := make(chan peer.AddrInfo)\n\n\tgo func() {\n\t\tdefer endSection(nil, \"watch topic ended\")\n\t\tdefer close(cpeers)\n\n\t\twgRefresh := sync.WaitGroup{}\n\n\t\tfor ctx.Err() == nil {\n\t\t\tif point == nil || time.Now().After(point.Deadline()) {\n\t\t\t\tpoint = s.rp.NewRendezvousPointForPeriod(time.Now(), base64.StdEncoding.EncodeToString(topic), seed)\n\t\t\t}\n\n\t\t\tbstrat := s.backoffFactory()\n\n\t\t\t// store watch peers information to be later used by the refresh method to force a lookup\n\t\t\ts.muRequest.Lock()\n\t\t\twctx, cancel := context.WithCancel(ctx)\n\t\t\ts.inprogressLookup[base64.StdEncoding.EncodeToString(topic)] = &swiperRequest{\n\t\t\t\tbstrat:    bstrat,\n\t\t\t\twgRefresh: &wgRefresh,\n\t\t\t\tctx:       wctx,\n\t\t\t\tout:       cpeers,\n\t\t\t\trdvTopic:  point.RotationTopic(),\n\t\t\t}\n\t\t\ts.muRequest.Unlock()\n\n\t\t\t// start looking for peers for the given rotation topic\n\t\t\ts.logger.Debug(\"looking for peers\", logutil.PrivateString(\"topic\", point.RotationTopic()))\n\t\t\tif err := s.watchPeers(wctx, bstrat, cpeers, point.RotationTopic()); err != nil && err != context.DeadlineExceeded {\n\t\t\t\ts.logger.Debug(\"watch until deadline ended\", zap.Error(err))\n\t\t\t}\n\t\t\tcancel()\n\n\t\t\t// at this point upper context is done or we need to refresh\n\t\t\t// rotation point.\n\t\t\t// take a little breath and wait one second to avoid calling find\n\t\t\t// peer in short amount of time\n\t\t\ttime.Sleep(time.Second)\n\t\t}\n\n\t\ts.muRequest.Lock()\n\t\tdelete(s.inprogressLookup, base64.StdEncoding.EncodeToString(topic))\n\t\ts.muRequest.Unlock()\n\n\t\t// wait all refresh job are done before closing the channel\n\t\t// we dont want to send peer on a closed channel\n\t\twgRefresh.Wait()\n\t}()\n\n\treturn cpeers\n}\n\nfunc (s *Swiper) watchPeers(ctx context.Context, _ backoff.BackoffStrategy, out chan<- peer.AddrInfo, topic string) error {\n\tsub := s.tinder.Subscribe(topic)\n\tdefer sub.Close()\n\t// func () {\n\t// \tif err := sub.Close(); err != nil {\n\t// \t\ts.logger.Error(\"unable to close sub properly\", zap.Error(err))\n\t// \t}\n\t// }()\n\n\ts.logger.Debug(\"swipper watch peers started\", logutil.PrivateString(\"topic\", topic))\n\n\t// start findpeers for pulls\n\tgo func() {\n\t\ttimeout := time.Minute // @TODO(gfanton): do we need to use backoffstartegy here ?\n\t\tfor ctx.Err() == nil {\n\t\t\ts.logger.Debug(\"swiper pulling for peers\", logutil.PrivateString(\"topic\", topic))\n\t\t\tif err := sub.Pull(); err != nil {\n\t\t\t\ts.logger.Error(\"unable to pull for peer on subscription\", zap.Error(err))\n\t\t\t}\n\n\t\t\tselect {\n\t\t\tcase <-time.After(timeout):\n\t\t\tcase <-ctx.Done():\n\t\t\t}\n\t\t}\n\t}()\n\n\tfor {\n\t\t// wait until the context is done\n\t\tselect {\n\t\tcase p := <-sub.Out():\n\t\t\ts.logger.Debug(\"found a peers\", logutil.PrivateString(\"topic\", topic), zap.String(\"peer\", p.String()))\n\t\t\tout <- p\n\t\tcase <-ctx.Done():\n\t\t\ts.logger.Debug(\"watch peers done\", logutil.PrivateString(\"topic\", topic))\n\t\t\treturn ctx.Err()\n\t\t}\n\t}\n}\n\n// watch looks for peers providing a resource\nfunc (s *Swiper) Announce(ctx context.Context, topic, seed []byte) {\n\tvar point *rendezvous.Point\n\n\ts.logger.Debug(\"start announce for peer with\",\n\t\tlogutil.PrivateString(\"topic\", base64.StdEncoding.EncodeToString(topic)),\n\t\tlogutil.PrivateString(\"seed\", string(seed)))\n\n\tgo func() {\n\t\tfor ctx.Err() == nil {\n\t\t\tif point == nil || time.Now().After(point.Deadline()) {\n\t\t\t\tpoint = s.rp.NewRendezvousPointForPeriod(time.Now(), base64.StdEncoding.EncodeToString(topic), seed)\n\t\t\t}\n\n\t\t\ts.logger.Debug(\"self announce topic for time\", logutil.PrivateString(\"topic\", point.RotationTopic()))\n\n\t\t\tactx, cancel := context.WithDeadline(ctx, point.Deadline())\n\t\t\tif err := s.tinder.StartAdvertises(actx, point.RotationTopic()); err != nil && err != ctx.Err() {\n\t\t\t\tcancel()\n\t\t\t\t<-time.After(time.Second * 10) // retry after 10sc\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tselect {\n\t\t\tcase <-actx.Done():\n\t\t\t\ts.logger.Debug(\"rotation ended\", logutil.PrivateString(\"topic\", point.RotationTopic()))\n\t\t\tcase <-ctx.Done():\n\t\t\t\ts.logger.Debug(\"announce advertise ended\", logutil.PrivateString(\"topic\", point.RotationTopic()), zap.Error(ctx.Err()))\n\t\t\t}\n\n\t\t\tcancel()\n\t\t}\n\t}()\n}\n"
  },
  {
    "path": "tinder_swiper_test.go",
    "content": "package weshnet\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\tmocknet \"github.com/libp2p/go-libp2p/p2p/net/mock\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"berty.tech/weshnet/v2/pkg/ipfsutil\"\n\t\"berty.tech/weshnet/v2/pkg/rendezvous\"\n\t\"berty.tech/weshnet/v2/pkg/testutil\"\n\t\"berty.tech/weshnet/v2/pkg/tinder\"\n)\n\nfunc TestAnnounceWatchForPeriod(t *testing.T) {\n\ttestutil.FilterSpeed(t, testutil.Slow)\n\tcases := []struct {\n\t\texpectedPeersFound int\n\t\ttopicA             []byte\n\t\ttopicB             []byte\n\t\tseedA              []byte\n\t\tseedB              []byte\n\t}{\n\t\t{\n\t\t\texpectedPeersFound: 0,\n\t\t\ttopicA:             []byte(\"topicA\"),\n\t\t\ttopicB:             []byte(\"topicB\"),\n\t\t\tseedA:              []byte(\"seedA\"),\n\t\t\tseedB:              []byte(\"seedA\"),\n\t\t},\n\t\t{\n\t\t\texpectedPeersFound: 1,\n\t\t\ttopicA:             []byte(\"topicA\"),\n\t\t\ttopicB:             []byte(\"topicA\"),\n\t\t\tseedA:              []byte(\"seedA\"),\n\t\t\tseedB:              []byte(\"seedA\"),\n\t\t},\n\t}\n\n\tlogger, cleanup := testutil.Logger(t)\n\tdefer cleanup()\n\tfor i, tc := range cases {\n\t\tt.Run(fmt.Sprintf(\"tc: %d\", i), func(t *testing.T) {\n\t\t\tctx, cancel := context.WithCancel(context.Background())\n\t\t\tdefer cancel()\n\n\t\t\tmn := mocknet.New()\n\t\t\tdefer mn.Close()\n\n\t\t\topts := &ipfsutil.TestingAPIOpts{\n\t\t\t\tLogger:          logger,\n\t\t\t\tMocknet:         mn,\n\t\t\t\tDiscoveryServer: tinder.NewMockDriverServer(),\n\t\t\t}\n\n\t\t\tapiA := ipfsutil.TestingCoreAPIUsingMockNet(ctx, t, opts)\n\t\t\tapiB := ipfsutil.TestingCoreAPIUsingMockNet(ctx, t, opts)\n\n\t\t\terr := mn.LinkAll()\n\t\t\trequire.NoError(t, err)\n\t\t\terr = mn.ConnectAllButSelf()\n\t\t\trequire.NoError(t, err)\n\n\t\t\trpA := rendezvous.NewRotationInterval(time.Hour)\n\t\t\trpB := rendezvous.NewRotationInterval(time.Hour)\n\n\t\t\tswiperA := NewSwiper(opts.Logger, apiA.Tinder(), rpA)\n\t\t\tswiperB := NewSwiper(opts.Logger, apiB.Tinder(), rpB)\n\n\t\t\tswiperA.Announce(ctx, tc.topicA, tc.seedA)\n\n\t\t\ttime.Sleep(time.Millisecond * 100)\n\n\t\t\tcpeers := swiperB.WatchTopic(ctx, tc.topicB, tc.seedB)\n\n\t\t\tvar foundPeers int\n\t\tloop:\n\t\t\tfor foundPeers = 0; foundPeers < tc.expectedPeersFound; foundPeers++ {\n\t\t\t\tselect {\n\t\t\t\tcase <-ctx.Done():\n\t\t\t\t\tbreak loop\n\t\t\t\tcase <-cpeers:\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tassert.Equal(t, len(cpeers), 0)\n\t\t\tassert.Equal(t, tc.expectedPeersFound, foundPeers)\n\t\t})\n\t}\n}\n\nfunc TestAnnounceForPeriod(t *testing.T) {\n}\n"
  },
  {
    "path": "tool/bench-cellular/.gitignore",
    "content": "bench\n"
  },
  {
    "path": "tool/bench-cellular/Makefile",
    "content": "SRCS := $(wildcard *.go)\n\nbench: $(SRCS)\n\tgo build $(SRCS)\n\t@echo \"Now run './bench -h'\"\n\nhelp: bench\n\t./bench -h\n"
  },
  {
    "path": "tool/bench-cellular/bench.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\tcrand \"crypto/rand\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\tmrand \"math/rand\"\n\t\"os\"\n\t\"strings\"\n\n\tgolog \"github.com/ipfs/go-log\"\n\t\"github.com/libp2p/go-libp2p\"\n\tquict \"github.com/libp2p/go-libp2p-quic-transport\"\n\t\"github.com/libp2p/go-libp2p/core/crypto\"\n\ttcpt \"github.com/libp2p/go-libp2p/p2p/transport/tcp\"\n\tma \"github.com/multiformats/go-multiaddr\"\n\t\"github.com/peterbourgon/ff/v3/ffcli\"\n)\n\nconst (\n\tbenchDownloadPID = \"/bench/download/1.0.0\"\n\tbenchUploadPID   = \"/bench/upload/1.0.0\"\n)\n\ntype globalOpts struct {\n\ttcp         bool\n\tinsecure    bool\n\tseed        int64\n\tverbose     bool\n\tveryVerbose bool\n}\n\nfunc globalOptsToLibp2pOpts(gOpts *globalOpts) ([]libp2p.Option, error) {\n\tvar (\n\t\tr    io.Reader\n\t\topts []libp2p.Option\n\t)\n\n\tif gOpts.seed == 0 {\n\t\tr = crand.Reader\n\t} else {\n\t\tr = mrand.New(mrand.NewSource(gOpts.seed))\n\t}\n\n\tpriv, _, err := crypto.GenerateKeyPairWithReader(crypto.RSA, crypto.MinRsaKeyBits, r)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\topts = append(opts, libp2p.Identity(priv))\n\n\tif gOpts.tcp {\n\t\topts = append(opts, libp2p.Transport(tcpt.NewTCPTransport))\n\t} else {\n\t\topts = append(opts, libp2p.Transport(quict.NewTransport))\n\t}\n\n\tif gOpts.insecure {\n\t\topts = append(opts, libp2p.NoSecurity)\n\t}\n\n\treturn opts, nil\n}\n\nfunc main() {\n\tvar (\n\t\tctx   = context.Background()\n\t\tgOpts = &globalOpts{}\n\t\tsOpts = &serverOpts{}\n\t\tcOpts = &clientOpts{}\n\t)\n\n\tvar serverCommand *ffcli.Command\n\t{\n\t\tserverFs := flag.NewFlagSet(\"server\", flag.ExitOnError)\n\t\tserverFs.IntVar(&sOpts.port, \"port\", 0, \"port to listen on (default: random)\")\n\t\tserverFs.BoolVar(&sOpts.ip6, \"ip6\", false, \"use ipv6 instead of ipv4\")\n\t\tserverFs.StringVar(&sOpts.relay, \"relay\", staticBertyRelayMode, fmt.Sprintf(\"set relay mode, possible values: '%s', '%s', '%s' or '%s'\", staticBertyRelayMode, staticIPFSRelayMode, discoveryRelayMode, disabledRelayMode))\n\n\t\tserverCommand = &ffcli.Command{\n\t\t\tName:       \"server\",\n\t\t\tShortUsage: \"bench server [flags]\",\n\t\t\tShortHelp:  \"run a benchmark server that listen for client request\",\n\t\t\tFlagSet:    serverFs,\n\t\t\tExec: func(ctx context.Context, args []string) error {\n\t\t\t\tif len(args) > 0 {\n\t\t\t\t\treturn flag.ErrHelp\n\t\t\t\t}\n\t\t\t\tif sOpts.relay != staticBertyRelayMode && sOpts.relay != staticIPFSRelayMode && sOpts.relay != discoveryRelayMode && sOpts.relay != disabledRelayMode {\n\t\t\t\t\tfmt.Fprintf(os.Stderr, \"error: invalid value for -relay flag: %s\\n\\n\", sOpts.relay)\n\t\t\t\t\treturn flag.ErrHelp\n\t\t\t\t}\n\n\t\t\t\tif gOpts.verbose {\n\t\t\t\t\tgolog.SetAllLoggers(golog.LevelError)\n\t\t\t\t\tgolog.SetLogLevel(\"autorelay\", \"debug\")\n\t\t\t\t\tgolog.SetLogLevel(\"autonat\", \"debug\")\n\t\t\t\t\tgolog.SetLogLevel(\"basichost\", \"debug\")\n\t\t\t\t\tgolog.SetLogLevel(\"swarm2\", \"debug\")\n\t\t\t\t}\n\t\t\t\tif gOpts.veryVerbose {\n\t\t\t\t\tgolog.SetAllLoggers(golog.LevelDebug)\n\t\t\t\t}\n\n\t\t\t\treturn runServer(ctx, gOpts, sOpts)\n\t\t\t},\n\t\t}\n\t}\n\n\tvar clientCommand *ffcli.Command\n\t{\n\t\tconst megabyte = 1048576\n\n\t\tclientFs := flag.NewFlagSet(\"client\", flag.ExitOnError)\n\t\tclientFs.StringVar(&cOpts.dest, \"dest\", \"\", \"server multiaddr to dial\")\n\t\tclientFs.StringVar(&cOpts.request, \"request\", fmt.Sprintf(\"%s,%s,%s\", pingRequestType, uploadRequestType, downloadRequestType), fmt.Sprintf(\"comma separated list of request type to send, possible values: '%s', '%s' and '%s'\", pingRequestType, uploadRequestType, downloadRequestType))\n\t\tclientFs.BoolVar(&cOpts.reco, \"reco\", false, \"test reconnection to server\")\n\t\tclientFs.IntVar(&cOpts.size, \"size\", megabyte, \"size (in bytes) of data to upload / download (default: 1MB)\")\n\n\t\tclientCommand = &ffcli.Command{\n\t\t\tName:       \"client\",\n\t\t\tShortUsage: \"bench client [flags]\",\n\t\t\tShortHelp:  \"run a benchmark client that send request to server\",\n\t\t\tFlagSet:    clientFs,\n\t\t\tExec: func(ctx context.Context, args []string) error {\n\t\t\t\tif len(args) > 0 {\n\t\t\t\t\treturn flag.ErrHelp\n\t\t\t\t}\n\n\t\t\t\trequestTypes := strings.Split(cOpts.request, \",\")\n\t\t\t\tif len(requestTypes) == 0 {\n\t\t\t\t\tfmt.Fprintf(os.Stderr, \"error: at least one request type must be specified using -request flag\\n\\n\")\n\t\t\t\t\treturn flag.ErrHelp\n\t\t\t\t}\n\t\t\t\tfor _, requestType := range requestTypes {\n\t\t\t\t\ttrimed := strings.TrimSpace(requestType)\n\t\t\t\t\tif trimed != pingRequestType && trimed != uploadRequestType && trimed != downloadRequestType {\n\t\t\t\t\t\tfmt.Fprintf(os.Stderr, \"error: invalid request type specified using -request flag: '%s'\\n\\n\", trimed)\n\t\t\t\t\t\treturn flag.ErrHelp\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif cOpts.dest == \"\" {\n\t\t\t\t\tfmt.Fprintf(os.Stderr, \"error: a server multiaddr must be specified using -dest flag\\n\\n\")\n\t\t\t\t\treturn flag.ErrHelp\n\t\t\t\t}\n\t\t\t\tif _, err := ma.NewMultiaddr(cOpts.dest); err != nil {\n\t\t\t\t\tfmt.Fprintf(os.Stderr, \"error: invalid multiaddr specified using -dest flag: %v\\n\\n\", err)\n\t\t\t\t\treturn flag.ErrHelp\n\t\t\t\t}\n\t\t\t\tif cOpts.size <= 0 {\n\t\t\t\t\tfmt.Fprintf(os.Stderr, \"error: a positive bytes amount must be set using -size flag (default 1MB)\\n\\n\")\n\t\t\t\t\treturn flag.ErrHelp\n\t\t\t\t}\n\n\t\t\t\tif gOpts.verbose {\n\t\t\t\t\tgolog.SetAllLoggers(golog.LevelError)\n\t\t\t\t\tgolog.SetLogLevel(\"basichost\", \"debug\")\n\t\t\t\t\tgolog.SetLogLevel(\"swarm2\", \"debug\")\n\t\t\t\t}\n\t\t\t\tif gOpts.veryVerbose {\n\t\t\t\t\tgolog.SetAllLoggers(golog.LevelDebug)\n\t\t\t\t}\n\n\t\t\t\treturn runClient(ctx, gOpts, cOpts)\n\t\t\t},\n\t\t}\n\t}\n\n\tvar rootCommand *ffcli.Command\n\t{\n\t\trootFs := flag.NewFlagSet(\"root\", flag.ExitOnError)\n\t\trootFs.BoolVar(&gOpts.tcp, \"tcp\", false, \"use TCP instead of QUIC\")\n\t\trootFs.BoolVar(&gOpts.insecure, \"insecure\", false, \"use an unencrypted connection\")\n\t\trootFs.Int64Var(&gOpts.seed, \"seed\", 0, \"set random seed for id generation\")\n\t\trootFs.BoolVar(&gOpts.verbose, \"v\", false, \"verbose mode: print debug level for relevant libp2p loggers\")\n\t\trootFs.BoolVar(&gOpts.veryVerbose, \"vv\", false, \"very verbose mode: print debug level for all libp2p loggers\")\n\n\t\trootCommand = &ffcli.Command{\n\t\t\tShortUsage: \"bench [flags] <subcommand> [subcommand_flags]\",\n\t\t\tFlagSet:    rootFs,\n\t\t\tExec:       func(context.Context, []string) error { return flag.ErrHelp },\n\t\t\tSubcommands: []*ffcli.Command{\n\t\t\t\tserverCommand,\n\t\t\t\tclientCommand,\n\t\t\t},\n\t\t}\n\t}\n\n\terr := rootCommand.ParseAndRun(ctx, os.Args[1:])\n\tif err == flag.ErrHelp {\n\t\tos.Exit(1)\n\t} else if err != nil {\n\t\tfmt.Fprintf(os.Stderr, \"error: %v\\n\", err)\n\t\tos.Exit(2)\n\t}\n}\n"
  },
  {
    "path": "tool/bench-cellular/client.go",
    "content": "package main\n\nimport (\n\t\"bufio\"\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"math/rand\"\n\t\"os\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/libp2p/go-libp2p\"\n\tpeer \"github.com/libp2p/go-libp2p-peer\"\n\t\"github.com/libp2p/go-libp2p/core/host\"\n\tpeerstore \"github.com/libp2p/go-libp2p/p2p/host/peerstore\"\n\tp2pping \"github.com/libp2p/go-libp2p/p2p/protocol/ping\"\n\tma \"github.com/multiformats/go-multiaddr\"\n)\n\nconst (\n\tpingRequestType     = \"ping\"\n\tuploadRequestType   = \"upload\"\n\tdownloadRequestType = \"download\"\n)\n\ntype clientOpts struct {\n\tdest    string\n\trequest string\n\treco    bool\n\tsize    int\n}\n\nfunc createClientHost(ctx context.Context, gOpts *globalOpts) (host.Host, error) {\n\topts, err := globalOptsToLibp2pOpts(gOpts) // Get identity and transport\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\topts = append(\n\t\topts,\n\t\tlibp2p.ListenAddrs(), // On client mode, set no listener\n\t)\n\n\treturn libp2p.New(ctx, opts...) // Create host\n}\n\nfunc addDestToPeerstore(h host.Host, dest string) (peer.ID, error) {\n\tmaddr, err := ma.NewMultiaddr(dest)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tvar pid string\n\tif _, err := maddr.ValueForProtocol(ma.P_CIRCUIT); err == nil {\n\t\tfirst := true\n\t\t// Get the second peerid (target), the first being the relay peerid\n\t\tma.ForEach(maddr, func(c ma.Component) bool {\n\t\t\tif c.Protocol().Code == ma.P_IPFS {\n\t\t\t\tif first {\n\t\t\t\t\tfirst = false\n\t\t\t\t} else {\n\t\t\t\t\tpid = c.Value()\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true\n\t\t})\n\t} else {\n\t\tpid, err = maddr.ValueForProtocol(ma.P_IPFS)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t}\n\n\tpeerid, err := peer.IDB58Decode(pid)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tif _, err := maddr.ValueForProtocol(ma.P_CIRCUIT); err != nil {\n\t\ttargetAddr, _ := ma.NewMultiaddr(fmt.Sprintf(\"/ipfs/%s\", pid))\n\t\tmaddr = maddr.Decapsulate(targetAddr)\n\t}\n\n\th.Peerstore().AddAddr(peerid, maddr, peerstore.PermanentAddrTTL)\n\n\treturn peerid, nil\n}\n\nfunc ping(ctx context.Context, h host.Host, peerid peer.ID) error {\n\tvar (\n\t\ttimeout            = 30 * time.Second\n\t\ttimeoutCtx, cancel = context.WithTimeout(ctx, timeout)\n\t\tresultOccured      = 0\n\t\tresultRequired     = 8\n\t\tstart              = time.Now()\n\t)\n\tdefer cancel()\n\n\tlog.Printf(\"New ping started with timeout: %v\", timeout)\n\n\tfor result := range p2pping.Ping(timeoutCtx, h, peerid) {\n\t\tif result.Error != nil {\n\t\t\treturn fmt.Errorf(\"ping error: %v\", result.Error)\n\t\t}\n\t\tlog.Printf(\"\\tPing RTT: %v\", result.RTT)\n\n\t\tresultOccured++\n\t\tif resultOccured >= resultRequired {\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif resultOccured < resultRequired {\n\t\treturn fmt.Errorf(\"ping request timeouted after %v: %d/%d (RTT done/required)\", timeout, resultOccured, resultRequired)\n\t}\n\tlog.Printf(\"Ping request with %d RTT took: %v\", resultOccured, time.Since(start))\n\n\treturn nil\n}\n\nfunc upload(ctx context.Context, h host.Host, peerid peer.ID, cOpts *clientOpts) error {\n\tstart := time.Now()\n\tsu, err := h.NewStream(ctx, peerid, benchUploadPID)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"new upload stream failed: %v\", err)\n\t}\n\n\treader := bufio.NewReader(su)\n\tif _, err = reader.ReadString('\\n'); err != nil {\n\t\treturn fmt.Errorf(\"read error during stream opened ack: %v\", err)\n\t}\n\tlog.Printf(\"New upload stream took: %v\", time.Since(start))\n\n\tdata := make([]byte, cOpts.size)\n\trand.Read(data)\n\n\tstart = time.Now()\n\tif _, err = su.Write(data); err != nil {\n\t\treturn fmt.Errorf(\"write error during upload: %v\", err)\n\t}\n\tsu.CloseWrite()\n\n\tif _, err = reader.ReadString('\\n'); err != nil {\n\t\treturn fmt.Errorf(\"read error during uploaded ack: %v\", err)\n\t}\n\tlog.Printf(\"Data (%d bytes) upload took: %v\", cOpts.size, time.Since(start))\n\n\tsu.CloseRead()\n\n\treturn nil\n}\n\nfunc download(ctx context.Context, h host.Host, peerid peer.ID, cOpts *clientOpts) error {\n\tstart := time.Now()\n\tsd, err := h.NewStream(ctx, peerid, benchDownloadPID)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"new download stream failed: %v\", err)\n\t}\n\n\treader := bufio.NewReader(sd)\n\tif _, err = reader.ReadString('\\n'); err != nil {\n\t\treturn fmt.Errorf(\"read error during stream opened ack: %v\", err)\n\t}\n\tlog.Printf(\"New download stream took: %v\", time.Since(start))\n\n\t// Send size to download\n\tsizeStr := fmt.Sprintf(\"%d\\n\", cOpts.size)\n\tif _, err = sd.Write([]byte(sizeStr)); err != nil {\n\t\treturn fmt.Errorf(\"write size error during download: %v\", err)\n\t}\n\n\tstart = time.Now()\n\tif _, err = io.ReadAll(sd); err != nil {\n\t\treturn err\n\t}\n\tlog.Printf(\"Data (%d bytes) download took: %v\", cOpts.size, time.Since(start))\n\n\tsd.Close()\n\n\treturn nil\n}\n\nfunc runClient(ctx context.Context, gOpts *globalOpts, cOpts *clientOpts) error {\n\th, err := createClientHost(ctx, gOpts)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"client host creation failed: %v\", err)\n\t}\n\n\tlog.Println(\"Local peerID:\", h.ID().String())\n\n\tpeerid, err := addDestToPeerstore(h, cOpts.dest)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\trequestList := strings.Split(cOpts.request, \",\")\n\tfor i, request := range requestList {\n\t\trequestList[i] = strings.TrimSpace(request)\n\t}\n\n\tfor {\n\t\tlog.Printf(\"Playing request list: %q\", requestList)\n\t\tstart := time.Now()\n\t\tfor _, request := range requestList {\n\t\t\tswitch request {\n\t\t\tcase pingRequestType:\n\t\t\t\terr = ping(ctx, h, peerid)\n\t\t\tcase uploadRequestType:\n\t\t\t\terr = upload(ctx, h, peerid, cOpts)\n\t\t\tcase downloadRequestType:\n\t\t\t\terr = download(ctx, h, peerid, cOpts)\n\t\t\t}\n\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tlog.Printf(\"Playing request list took: %v\", time.Since(start))\n\n\t\tif cOpts.reco {\n\t\t\tcOpts.reco = false\n\t\t\treader := bufio.NewReader(os.Stdin)\n\t\t\tfmt.Printf(\"%s Reconnection test: switch connection then press enter... \", time.Now().Format(\"2006/01/02 15:04:05\"))\n\t\t\t_, _ = reader.ReadString('\\n')\n\t\t\tlog.Print(\"Reconnection test: replay request list using new connection\")\n\t\t\tcontinue\n\t\t}\n\n\t\tbreak\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "tool/bench-cellular/go.mod",
    "content": "module berty.tech/weshnet/tool/bench-cellular\n\ngo 1.15\n\nrequire (\n\tgithub.com/ipfs/go-log v1.0.4\n\tgithub.com/libp2p/go-libp2p v0.13.0\n\tgithub.com/libp2p/go-libp2p/core v0.8.0\n\tgithub.com/libp2p/go-libp2p-kad-dht v0.11.1\n\tgithub.com/libp2p/go-libp2p-peer v0.2.0\n\tgithub.com/libp2p/go-libp2p-peerstore v0.2.6\n\tgithub.com/libp2p/go-libp2p-quic-transport v0.10.0\n\tgithub.com/libp2p/go-libp2p-routing v0.1.0\n\tgithub.com/libp2p/go-tcp-transport v0.2.1\n\tgithub.com/multiformats/go-multiaddr v0.3.1\n\tgithub.com/peterbourgon/ff/v3 v3.0.0\n)\n"
  },
  {
    "path": "tool/bench-cellular/go.sum",
    "content": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo=\ndmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=\ndmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=\ndmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=\ndmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=\ngit.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=\ngithub.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=\ngithub.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=\ngithub.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/Kubuxu/go-os-helper v0.0.1/go.mod h1:N8B+I7vPCT80IcP58r50u4+gEEcsZETFUpAzWW2ep1Y=\ngithub.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=\ngithub.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=\ngithub.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=\ngithub.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=\ngithub.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=\ngithub.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=\ngithub.com/btcsuite/btcd v0.0.0-20190213025234-306aecffea32/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8=\ngithub.com/btcsuite/btcd v0.0.0-20190523000118-16327141da8c/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI=\ngithub.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI=\ngithub.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw=\ngithub.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=\ngithub.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=\ngithub.com/btcsuite/btcutil v0.0.0-20190207003914-4c204d697803/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=\ngithub.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=\ngithub.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=\ngithub.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=\ngithub.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=\ngithub.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=\ngithub.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=\ngithub.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\ngithub.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=\ngithub.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=\ngithub.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=\ngithub.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=\ngithub.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=\ngithub.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=\ngithub.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=\ngithub.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=\ngithub.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=\ngithub.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=\ngithub.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davidlazar/go-crypto v0.0.0-20170701192655-dcfb0a7ac018 h1:6xT9KW8zLC5IlbaIF5Q7JNieBoACT7iW0YTxQHR0in0=\ngithub.com/davidlazar/go-crypto v0.0.0-20170701192655-dcfb0a7ac018/go.mod h1:rQYf4tfk5sSwFsnDg3qYaBxSjsD9S8+59vW0dKUgme4=\ngithub.com/dgraph-io/badger v1.5.5-0.20190226225317-8115aed38f8f/go.mod h1:VZxzAIRPHRVNRKRo6AXrX9BJegn6il06VMTZVJYCIjQ=\ngithub.com/dgraph-io/badger v1.6.0-rc1/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4=\ngithub.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4=\ngithub.com/dgraph-io/badger v1.6.1/go.mod h1:FRmFw3uxvcpa8zG3Rxs0th+hCLIuaQg8HlNV5bjgnuU=\ngithub.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=\ngithub.com/dgryski/go-farm v0.0.0-20190104051053-3adb47b1fb0f/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=\ngithub.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=\ngithub.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=\ngithub.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=\ngithub.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6 h1:u/UEqS66A5ckRmS4yNpjmVH56sVtS/RfclBAYocb4as=\ngithub.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6/go.mod h1:1i71OnUq3iUe1ma7Lr6yG6/rjvM3emb6yoL7xLFzcVQ=\ngithub.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk=\ngithub.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=\ngithub.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=\ngithub.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=\ngithub.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=\ngithub.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=\ngithub.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=\ngithub.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=\ngithub.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=\ngithub.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=\ngithub.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=\ngithub.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9 h1:uHTyIjqVhYRhLbJ8nIiOJHkEZZ+5YoOsAbD3sk82NiE=\ngithub.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc=\ngithub.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=\ngithub.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0=\ngithub.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=\ngithub.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=\ngithub.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=\ngithub.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=\ngithub.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=\ngithub.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=\ngithub.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=\ngithub.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=\ngithub.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=\ngithub.com/google/gopacket v1.1.17 h1:rMrlX2ZY2UbvT+sdz3+6J+pp2z+msCq9MxTU6ymxbBY=\ngithub.com/google/gopacket v1.1.17/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM=\ngithub.com/google/gopacket v1.1.18 h1:lum7VRA9kdlvBi7/v2p7/zcbkduHaCH/SVVyurs7OpY=\ngithub.com/google/gopacket v1.1.18/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM=\ngithub.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=\ngithub.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=\ngithub.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=\ngithub.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=\ngithub.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=\ngithub.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=\ngithub.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=\ngithub.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=\ngithub.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=\ngithub.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU=\ngithub.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48=\ngithub.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=\ngithub.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=\ngithub.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI=\ngithub.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=\ngithub.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=\ngithub.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=\ngithub.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=\ngithub.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=\ngithub.com/huin/goupnp v1.0.0 h1:wg75sLpL6DZqwHQN6E1Cfk6mtfzS45z8OV+ic+DtHRo=\ngithub.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc=\ngithub.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o=\ngithub.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=\ngithub.com/ipfs/go-cid v0.0.1/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=\ngithub.com/ipfs/go-cid v0.0.2/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=\ngithub.com/ipfs/go-cid v0.0.3/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=\ngithub.com/ipfs/go-cid v0.0.4/go.mod h1:4LLaPOQwmk5z9LBgQnpkivrx8BJjUyGwTXCd5Xfj6+M=\ngithub.com/ipfs/go-cid v0.0.5/go.mod h1:plgt+Y5MnOey4vO4UlUazGqdbEXuFYitED67FexhXog=\ngithub.com/ipfs/go-cid v0.0.6/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I=\ngithub.com/ipfs/go-cid v0.0.7 h1:ysQJVJA3fNDF1qigJbsSQOdjhVLsOEoPdh0+R97k3jY=\ngithub.com/ipfs/go-cid v0.0.7/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I=\ngithub.com/ipfs/go-datastore v0.0.1/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE=\ngithub.com/ipfs/go-datastore v0.1.0/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE=\ngithub.com/ipfs/go-datastore v0.1.1/go.mod h1:w38XXW9kVFNp57Zj5knbKWM2T+KOZCGDRVNdgPHtbHw=\ngithub.com/ipfs/go-datastore v0.4.0/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA=\ngithub.com/ipfs/go-datastore v0.4.1/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA=\ngithub.com/ipfs/go-datastore v0.4.4/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA=\ngithub.com/ipfs/go-datastore v0.4.5 h1:cwOUcGMLdLPWgu3SlrCckCMznaGADbPqE0r8h768/Dg=\ngithub.com/ipfs/go-datastore v0.4.5/go.mod h1:eXTcaaiN6uOlVCLS9GjJUJtlvJfM3xk23w3fyfrmmJs=\ngithub.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk=\ngithub.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps=\ngithub.com/ipfs/go-ds-badger v0.0.2/go.mod h1:Y3QpeSFWQf6MopLTiZD+VT6IC1yZqaGmjvRcKeSGij8=\ngithub.com/ipfs/go-ds-badger v0.0.5/go.mod h1:g5AuuCGmr7efyzQhLL8MzwqcauPojGPUaHzfGTzuE3s=\ngithub.com/ipfs/go-ds-badger v0.0.7/go.mod h1:qt0/fWzZDoPW6jpQeqUjR5kBfhDNB65jd9YlmAvpQBk=\ngithub.com/ipfs/go-ds-badger v0.2.1/go.mod h1:Tx7l3aTph3FMFrRS838dcSJh+jjA7cX9DrGVwx/NOwE=\ngithub.com/ipfs/go-ds-badger v0.2.3/go.mod h1:pEYw0rgg3FIrywKKnL+Snr+w/LjJZVMTBRn4FS6UHUk=\ngithub.com/ipfs/go-ds-leveldb v0.0.1/go.mod h1:feO8V3kubwsEF22n0YRQCffeb79OOYIykR4L04tMOYc=\ngithub.com/ipfs/go-ds-leveldb v0.1.0/go.mod h1:hqAW8y4bwX5LWcCtku2rFNX3vjDZCy5LZCg+cSZvYb8=\ngithub.com/ipfs/go-ds-leveldb v0.4.1/go.mod h1:jpbku/YqBSsBc1qgME8BkWS4AxzF2cEu1Ii2r79Hh9s=\ngithub.com/ipfs/go-ds-leveldb v0.4.2/go.mod h1:jpbku/YqBSsBc1qgME8BkWS4AxzF2cEu1Ii2r79Hh9s=\ngithub.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw=\ngithub.com/ipfs/go-ipfs-util v0.0.1/go.mod h1:spsl5z8KUnrve+73pOhSVZND1SIxPW5RyBCNzQxlJBc=\ngithub.com/ipfs/go-ipfs-util v0.0.2 h1:59Sswnk1MFaiq+VcaknX7aYEyGyGDAA73ilhEK2POp8=\ngithub.com/ipfs/go-ipfs-util v0.0.2/go.mod h1:CbPtkWJzjLdEcezDns2XYaehFVNXG9zrdrtMecczcsQ=\ngithub.com/ipfs/go-ipns v0.0.2 h1:oq4ErrV4hNQ2Eim257RTYRgfOSV/s8BDaf9iIl4NwFs=\ngithub.com/ipfs/go-ipns v0.0.2/go.mod h1:WChil4e0/m9cIINWLxZe1Jtf77oz5L05rO2ei/uKJ5U=\ngithub.com/ipfs/go-log v0.0.1/go.mod h1:kL1d2/hzSpI0thNYjiKfjanbVNU+IIGA/WnNESY9leM=\ngithub.com/ipfs/go-log v1.0.2/go.mod h1:1MNjMxe0u6xvJZgeqbJ8vdo2TKaGwZ1a0Bpza+sr2Sk=\ngithub.com/ipfs/go-log v1.0.3/go.mod h1:OsLySYkwIbiSUR/yBTdv1qPtcE4FW3WPWk/ewz9Ru+A=\ngithub.com/ipfs/go-log v1.0.4 h1:6nLQdX4W8P9yZZFH7mO+X/PzjN8Laozm/lMJ6esdgzY=\ngithub.com/ipfs/go-log v1.0.4/go.mod h1:oDCg2FkjogeFOhqqb+N39l2RpTNPL6F/StPkB3kPgcs=\ngithub.com/ipfs/go-log/v2 v2.0.2/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0=\ngithub.com/ipfs/go-log/v2 v2.0.3/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0=\ngithub.com/ipfs/go-log/v2 v2.0.5/go.mod h1:eZs4Xt4ZUJQFM3DlanGhy7TkwwawCZcSByscwkWG+dw=\ngithub.com/ipfs/go-log/v2 v2.1.1 h1:G4TtqN+V9y9HY9TA6BwbCVyyBZ2B9MbCjR2MtGx8FR0=\ngithub.com/ipfs/go-log/v2 v2.1.1/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHntrv9KM=\ngithub.com/jackpal/gateway v1.0.5/go.mod h1:lTpwd4ACLXmpyiCTRtfiNyVnUmqT9RivzCDQetPfnjA=\ngithub.com/jackpal/go-nat-pmp v1.0.1/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=\ngithub.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=\ngithub.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=\ngithub.com/jbenet/go-cienv v0.0.0-20150120210510-1bb1476777ec/go.mod h1:rGaEvXB4uRSZMmzKNLoXvTu1sfx+1kv/DojUlPrSZGs=\ngithub.com/jbenet/go-cienv v0.1.0 h1:Vc/s0QbQtoxX8MwwSLWWh+xNNZvM3Lw7NsTcHrvvhMc=\ngithub.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA=\ngithub.com/jbenet/go-temp-err-catcher v0.0.0-20150120210811-aac704a3f4f2/go.mod h1:8GXXJV31xl8whumTzdZsTt3RnUIiPqzkyf7mxToRCMs=\ngithub.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABoLk/+KKHggpk=\ngithub.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk=\ngithub.com/jbenet/goprocess v0.0.0-20160826012719-b497e2f366b8/go.mod h1:Ly/wlsjFq/qrU3Rar62tu1gASgGw6chQbSh/XgIIXCY=\ngithub.com/jbenet/goprocess v0.1.3/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4=\ngithub.com/jbenet/goprocess v0.1.4 h1:DRGOFReOMqqDNXwW70QkacFW0YN9QnwLV0Vqk+3oU0o=\ngithub.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4=\ngithub.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=\ngithub.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=\ngithub.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=\ngithub.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=\ngithub.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\ngithub.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=\ngithub.com/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d/go.mod h1:P2viExyCEfeWGU259JnaQ34Inuec4R38JCyBx2edgD0=\ngithub.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=\ngithub.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d h1:68u9r4wEvL3gYg2jvAOgROwZ3H+Y3hIDk4tbbmIjcYQ=\ngithub.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=\ngithub.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/libp2p/go-addr-util v0.0.1/go.mod h1:4ac6O7n9rIAKB1dnd+s8IbbMXkt+oBpzX4/+RACcnlQ=\ngithub.com/libp2p/go-addr-util v0.0.2 h1:7cWK5cdA5x72jX0g8iLrQWm5TRJZ6CzGdPEhWj7plWU=\ngithub.com/libp2p/go-addr-util v0.0.2/go.mod h1:Ecd6Fb3yIuLzq4bD7VcywcVSBtefcAwnUISBM3WG15E=\ngithub.com/libp2p/go-buffer-pool v0.0.1/go.mod h1:xtyIz9PMobb13WaxR6Zo1Pd1zXJKYg0a8KiIvDp3TzQ=\ngithub.com/libp2p/go-buffer-pool v0.0.2 h1:QNK2iAFa8gjAe1SPz6mHSMuCcjs+X1wlHzeOSqcmlfs=\ngithub.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM=\ngithub.com/libp2p/go-cidranger v1.1.0 h1:ewPN8EZ0dd1LSnrtuwd4709PXVcITVeuwbag38yPW7c=\ngithub.com/libp2p/go-cidranger v1.1.0/go.mod h1:KWZTfSr+r9qEo9OkI9/SIEeAtw+NNoU0dXIXt15Okic=\ngithub.com/libp2p/go-conn-security-multistream v0.1.0/go.mod h1:aw6eD7LOsHEX7+2hJkDxw1MteijaVcI+/eP2/x3J1xc=\ngithub.com/libp2p/go-conn-security-multistream v0.2.0 h1:uNiDjS58vrvJTg9jO6bySd1rMKejieG7v45ekqHbZ1M=\ngithub.com/libp2p/go-conn-security-multistream v0.2.0/go.mod h1:hZN4MjlNetKD3Rq5Jb/P5ohUnFLNzEAR4DLSzpn2QLU=\ngithub.com/libp2p/go-eventbus v0.1.0/go.mod h1:vROgu5cs5T7cv7POWlWxBaVLxfSegC5UGQf8A2eEmx4=\ngithub.com/libp2p/go-eventbus v0.2.1 h1:VanAdErQnpTioN2TowqNcOijf6YwhuODe4pPKSDpxGc=\ngithub.com/libp2p/go-eventbus v0.2.1/go.mod h1:jc2S4SoEVPP48H9Wpzm5aiGwUCBMfGhVhhBjyhhCJs8=\ngithub.com/libp2p/go-flow-metrics v0.0.1/go.mod h1:Iv1GH0sG8DtYN3SVJ2eG221wMiNpZxBdp967ls1g+k8=\ngithub.com/libp2p/go-flow-metrics v0.0.2/go.mod h1:HeoSNUrOJVK1jEpDqVEiUOIXqhbnS27omG0uWU5slZs=\ngithub.com/libp2p/go-flow-metrics v0.0.3 h1:8tAs/hSdNvUiLgtlSy3mxwxWP4I9y/jlkPFT7epKdeM=\ngithub.com/libp2p/go-flow-metrics v0.0.3/go.mod h1:HeoSNUrOJVK1jEpDqVEiUOIXqhbnS27omG0uWU5slZs=\ngithub.com/libp2p/go-libp2p v0.6.1/go.mod h1:CTFnWXogryAHjXAKEbOf1OWY+VeAP3lDMZkfEI5sT54=\ngithub.com/libp2p/go-libp2p v0.7.0/go.mod h1:hZJf8txWeCduQRDC/WSqBGMxaTHCOYHt2xSU1ivxn0k=\ngithub.com/libp2p/go-libp2p v0.7.4/go.mod h1:oXsBlTLF1q7pxr+9w6lqzS1ILpyHsaBPniVO7zIHGMw=\ngithub.com/libp2p/go-libp2p v0.8.1/go.mod h1:QRNH9pwdbEBpx5DTJYg+qxcVaDMAz3Ee/qDKwXujH5o=\ngithub.com/libp2p/go-libp2p v0.12.0/go.mod h1:FpHZrfC1q7nA8jitvdjKBDF31hguaC676g/nT9PgQM0=\ngithub.com/libp2p/go-libp2p v0.13.0 h1:tDdrXARSghmusdm0nf1U/4M8aj8Rr0V2IzQOXmbzQ3s=\ngithub.com/libp2p/go-libp2p v0.13.0/go.mod h1:pM0beYdACRfHO1WcJlp65WXyG2A6NqYM+t2DTVAJxMo=\ngithub.com/libp2p/go-libp2p-asn-util v0.0.0-20200825225859-85005c6cf052 h1:BM7aaOF7RpmNn9+9g6uTjGJ0cTzWr5j9i9IKeun2M8U=\ngithub.com/libp2p/go-libp2p-asn-util v0.0.0-20200825225859-85005c6cf052/go.mod h1:nRMRTab+kZuk0LnKZpxhOVH/ndsdr2Nr//Zltc/vwgo=\ngithub.com/libp2p/go-libp2p-autonat v0.1.1/go.mod h1:OXqkeGOY2xJVWKAGV2inNF5aKN/djNA3fdpCWloIudE=\ngithub.com/libp2p/go-libp2p-autonat v0.2.0/go.mod h1:DX+9teU4pEEoZUqR1PiMlqliONQdNbfzE1C718tcViI=\ngithub.com/libp2p/go-libp2p-autonat v0.2.1/go.mod h1:MWtAhV5Ko1l6QBsHQNSuM6b1sRkXrpk0/LqCr+vCVxI=\ngithub.com/libp2p/go-libp2p-autonat v0.2.2/go.mod h1:HsM62HkqZmHR2k1xgX34WuWDzk/nBwNHoeyyT4IWV6A=\ngithub.com/libp2p/go-libp2p-autonat v0.4.0 h1:3y8XQbpr+ssX8QfZUHekjHCYK64sj6/4hnf/awD4+Ug=\ngithub.com/libp2p/go-libp2p-autonat v0.4.0/go.mod h1:YxaJlpr81FhdOv3W3BTconZPfhaYivRdf53g+S2wobk=\ngithub.com/libp2p/go-libp2p-blankhost v0.1.1/go.mod h1:pf2fvdLJPsC1FsVrNP3DUUvMzUts2dsLLBEpo1vW1ro=\ngithub.com/libp2p/go-libp2p-blankhost v0.1.4/go.mod h1:oJF0saYsAXQCSfDq254GMNmLNz6ZTHTOvtF4ZydUvwU=\ngithub.com/libp2p/go-libp2p-blankhost v0.2.0 h1:3EsGAi0CBGcZ33GwRuXEYJLLPoVWyXJ1bcJzAJjINkk=\ngithub.com/libp2p/go-libp2p-blankhost v0.2.0/go.mod h1:eduNKXGTioTuQAUcZ5epXi9vMl+t4d8ugUBRQ4SqaNQ=\ngithub.com/libp2p/go-libp2p-circuit v0.1.4/go.mod h1:CY67BrEjKNDhdTk8UgBX1Y/H5c3xkAcs3gnksxY7osU=\ngithub.com/libp2p/go-libp2p-circuit v0.2.1/go.mod h1:BXPwYDN5A8z4OEY9sOfr2DUQMLQvKt/6oku45YUmjIo=\ngithub.com/libp2p/go-libp2p-circuit v0.4.0 h1:eqQ3sEYkGTtybWgr6JLqJY6QLtPWRErvFjFDfAOO1wc=\ngithub.com/libp2p/go-libp2p-circuit v0.4.0/go.mod h1:t/ktoFIUzM6uLQ+o1G6NuBl2ANhBKN9Bc8jRIk31MoA=\ngithub.com/libp2p/go-libp2p-core v0.0.1/go.mod h1:g/VxnTZ/1ygHxH3dKok7Vno1VfpvGcGip57wjTU4fco=\ngithub.com/libp2p/go-libp2p-core v0.0.4/go.mod h1:jyuCQP356gzfCFtRKyvAbNkyeuxb7OlyhWZ3nls5d2I=\ngithub.com/libp2p/go-libp2p-core v0.2.0/go.mod h1:X0eyB0Gy93v0DZtSYbEM7RnMChm9Uv3j7yRXjO77xSI=\ngithub.com/libp2p/go-libp2p-core v0.2.2/go.mod h1:8fcwTbsG2B+lTgRJ1ICZtiM5GWCWZVoVrLaDRvIRng0=\ngithub.com/libp2p/go-libp2p-core v0.2.4/go.mod h1:STh4fdfa5vDYr0/SzYYeqnt+E6KfEV5VxfIrm0bcI0g=\ngithub.com/libp2p/go-libp2p-core v0.2.5/go.mod h1:6+5zJmKhsf7yHn1RbmYDu08qDUpIUxGdqHuEZckmZOA=\ngithub.com/libp2p/go-libp2p-core v0.3.0/go.mod h1:ACp3DmS3/N64c2jDzcV429ukDpicbL6+TrrxANBjPGw=\ngithub.com/libp2p/go-libp2p-core v0.3.1/go.mod h1:thvWy0hvaSBhnVBaW37BvzgVV68OUhgJJLAa6almrII=\ngithub.com/libp2p/go-libp2p-core v0.4.0/go.mod h1:49XGI+kc38oGVwqSBhDEwytaAxgZasHhFfQKibzTls0=\ngithub.com/libp2p/go-libp2p-core v0.5.0/go.mod h1:49XGI+kc38oGVwqSBhDEwytaAxgZasHhFfQKibzTls0=\ngithub.com/libp2p/go-libp2p-core v0.5.1/go.mod h1:uN7L2D4EvPCvzSH5SrhR72UWbnSGpt5/a35Sm4upn4Y=\ngithub.com/libp2p/go-libp2p-core v0.5.3/go.mod h1:uN7L2D4EvPCvzSH5SrhR72UWbnSGpt5/a35Sm4upn4Y=\ngithub.com/libp2p/go-libp2p-core v0.5.4/go.mod h1:uN7L2D4EvPCvzSH5SrhR72UWbnSGpt5/a35Sm4upn4Y=\ngithub.com/libp2p/go-libp2p-core v0.5.5/go.mod h1:vj3awlOr9+GMZJFH9s4mpt9RHHgGqeHCopzbYKZdRjM=\ngithub.com/libp2p/go-libp2p-core v0.5.6/go.mod h1:txwbVEhHEXikXn9gfC7/UDDw7rkxuX0bJvM49Ykaswo=\ngithub.com/libp2p/go-libp2p-core v0.5.7/go.mod h1:txwbVEhHEXikXn9gfC7/UDDw7rkxuX0bJvM49Ykaswo=\ngithub.com/libp2p/go-libp2p-core v0.6.0/go.mod h1:txwbVEhHEXikXn9gfC7/UDDw7rkxuX0bJvM49Ykaswo=\ngithub.com/libp2p/go-libp2p-core v0.6.1/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8=\ngithub.com/libp2p/go-libp2p-core v0.7.0/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8=\ngithub.com/libp2p/go-libp2p-core v0.8.0 h1:5K3mT+64qDTKbV3yTdbMCzJ7O6wbNsavAEb8iqBvBcI=\ngithub.com/libp2p/go-libp2p-core v0.8.0/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8=\ngithub.com/libp2p/go-libp2p-crypto v0.1.0 h1:k9MFy+o2zGDNGsaoZl0MA3iZ75qXxr9OOoAZF+sD5OQ=\ngithub.com/libp2p/go-libp2p-crypto v0.1.0/go.mod h1:sPUokVISZiy+nNuTTH/TY+leRSxnFj/2GLjtOTW90hI=\ngithub.com/libp2p/go-libp2p-discovery v0.2.0/go.mod h1:s4VGaxYMbw4+4+tsoQTqh7wfxg97AEdo4GYBt6BadWg=\ngithub.com/libp2p/go-libp2p-discovery v0.3.0/go.mod h1:o03drFnz9BVAZdzC/QUQ+NeQOu38Fu7LJGEOK2gQltw=\ngithub.com/libp2p/go-libp2p-discovery v0.5.0 h1:Qfl+e5+lfDgwdrXdu4YNCWyEo3fWuP+WgN9mN0iWviQ=\ngithub.com/libp2p/go-libp2p-discovery v0.5.0/go.mod h1:+srtPIU9gDaBNu//UHvcdliKBIcr4SfDcm0/PfPJLug=\ngithub.com/libp2p/go-libp2p-kad-dht v0.11.1 h1:FsriVQhOUZpCotWIjyFSjEDNJmUzuMma/RyyTDZanwc=\ngithub.com/libp2p/go-libp2p-kad-dht v0.11.1/go.mod h1:5ojtR2acDPqh/jXf5orWy8YGb8bHQDS+qeDcoscL/PI=\ngithub.com/libp2p/go-libp2p-kbucket v0.4.7 h1:spZAcgxifvFZHBD8tErvppbnNiKA5uokDu3CV7axu70=\ngithub.com/libp2p/go-libp2p-kbucket v0.4.7/go.mod h1:XyVo99AfQH0foSf176k4jY1xUJ2+jUJIZCSDm7r2YKk=\ngithub.com/libp2p/go-libp2p-loggables v0.1.0 h1:h3w8QFfCt2UJl/0/NW4K829HX/0S4KD31PQ7m8UXXO8=\ngithub.com/libp2p/go-libp2p-loggables v0.1.0/go.mod h1:EyumB2Y6PrYjr55Q3/tiJ/o3xoDasoRYM7nOzEpoa90=\ngithub.com/libp2p/go-libp2p-mplex v0.2.0/go.mod h1:Ejl9IyjvXJ0T9iqUTE1jpYATQ9NM3g+OtR+EMMODbKo=\ngithub.com/libp2p/go-libp2p-mplex v0.2.1/go.mod h1:SC99Rxs8Vuzrf/6WhmH41kNn13TiYdAWNYHrwImKLnE=\ngithub.com/libp2p/go-libp2p-mplex v0.2.2/go.mod h1:74S9eum0tVQdAfFiKxAyKzNdSuLqw5oadDq7+L/FELo=\ngithub.com/libp2p/go-libp2p-mplex v0.2.3/go.mod h1:CK3p2+9qH9x+7ER/gWWDYJ3QW5ZxWDkm+dVvjfuG3ek=\ngithub.com/libp2p/go-libp2p-mplex v0.3.0/go.mod h1:l9QWxRbbb5/hQMECEb908GbS9Sm2UAR2KFZKUJEynEs=\ngithub.com/libp2p/go-libp2p-mplex v0.4.0/go.mod h1:yCyWJE2sc6TBTnFpjvLuEJgTSw/u+MamvzILKdX7asw=\ngithub.com/libp2p/go-libp2p-mplex v0.4.1 h1:/pyhkP1nLwjG3OM+VuaNJkQT/Pqq73WzB3aDN3Fx1sc=\ngithub.com/libp2p/go-libp2p-mplex v0.4.1/go.mod h1:cmy+3GfqfM1PceHTLL7zQzAAYaryDu6iPSC+CIb094g=\ngithub.com/libp2p/go-libp2p-nat v0.0.5/go.mod h1:1qubaE5bTZMJE+E/uu2URroMbzdubFz1ChgiN79yKPE=\ngithub.com/libp2p/go-libp2p-nat v0.0.6 h1:wMWis3kYynCbHoyKLPBEMu4YRLltbm8Mk08HGSfvTkU=\ngithub.com/libp2p/go-libp2p-nat v0.0.6/go.mod h1:iV59LVhB3IkFvS6S6sauVTSOrNEANnINbI/fkaLimiw=\ngithub.com/libp2p/go-libp2p-netutil v0.1.0 h1:zscYDNVEcGxyUpMd0JReUZTrpMfia8PmLKcKF72EAMQ=\ngithub.com/libp2p/go-libp2p-netutil v0.1.0/go.mod h1:3Qv/aDqtMLTUyQeundkKsA+YCThNdbQD54k3TqjpbFU=\ngithub.com/libp2p/go-libp2p-noise v0.1.1 h1:vqYQWvnIcHpIoWJKC7Al4D6Hgj0H012TuXRhPwSMGpQ=\ngithub.com/libp2p/go-libp2p-noise v0.1.1/go.mod h1:QDFLdKX7nluB7DEnlVPbz7xlLHdwHFA9HiohJRr3vwM=\ngithub.com/libp2p/go-libp2p-peer v0.2.0 h1:EQ8kMjaCUwt/Y5uLgjT8iY2qg0mGUT0N1zUjer50DsY=\ngithub.com/libp2p/go-libp2p-peer v0.2.0/go.mod h1:RCffaCvUyW2CJmG2gAWVqwePwW7JMgxjsHm7+J5kjWY=\ngithub.com/libp2p/go-libp2p-peerstore v0.1.0/go.mod h1:2CeHkQsr8svp4fZ+Oi9ykN1HBb6u0MOvdJ7YIsmcwtY=\ngithub.com/libp2p/go-libp2p-peerstore v0.1.3/go.mod h1:BJ9sHlm59/80oSkpWgr1MyY1ciXAXV397W6h1GH/uKI=\ngithub.com/libp2p/go-libp2p-peerstore v0.1.4/go.mod h1:+4BDbDiiKf4PzpANZDAT+knVdLxvqh7hXOujessqdzs=\ngithub.com/libp2p/go-libp2p-peerstore v0.2.0/go.mod h1:N2l3eVIeAitSg3Pi2ipSrJYnqhVnMNQZo9nkSCuAbnQ=\ngithub.com/libp2p/go-libp2p-peerstore v0.2.1/go.mod h1:NQxhNjWxf1d4w6PihR8btWIRjwRLBr4TYKfNgrUkOPA=\ngithub.com/libp2p/go-libp2p-peerstore v0.2.2/go.mod h1:NQxhNjWxf1d4w6PihR8btWIRjwRLBr4TYKfNgrUkOPA=\ngithub.com/libp2p/go-libp2p-peerstore v0.2.6 h1:2ACefBX23iMdJU9Ke+dcXt3w86MIryes9v7In4+Qq3U=\ngithub.com/libp2p/go-libp2p-peerstore v0.2.6/go.mod h1:ss/TWTgHZTMpsU/oKVVPQCGuDHItOpf2W8RxAi50P2s=\ngithub.com/libp2p/go-libp2p-pnet v0.2.0 h1:J6htxttBipJujEjz1y0a5+eYoiPcFHhSYHH6na5f0/k=\ngithub.com/libp2p/go-libp2p-pnet v0.2.0/go.mod h1:Qqvq6JH/oMZGwqs3N1Fqhv8NVhrdYcO0BW4wssv21LA=\ngithub.com/libp2p/go-libp2p-quic-transport v0.10.0 h1:koDCbWD9CCHwcHZL3/WEvP2A+e/o5/W5L3QS/2SPMA0=\ngithub.com/libp2p/go-libp2p-quic-transport v0.10.0/go.mod h1:RfJbZ8IqXIhxBRm5hqUEJqjiiY8xmEuq3HUDS993MkA=\ngithub.com/libp2p/go-libp2p-record v0.1.2/go.mod h1:pal0eNcT5nqZaTV7UGhqeGqxFgGdsU/9W//C8dqjQDk=\ngithub.com/libp2p/go-libp2p-record v0.1.3 h1:R27hoScIhQf/A8XJZ8lYpnqh9LatJ5YbHs28kCIfql0=\ngithub.com/libp2p/go-libp2p-record v0.1.3/go.mod h1:yNUff/adKIfPnYQXgp6FQmNu3gLJ6EMg7+/vv2+9pY4=\ngithub.com/libp2p/go-libp2p-routing v0.1.0 h1:hFnj3WR3E2tOcKaGpyzfP4gvFZ3t8JkQmbapN0Ct+oU=\ngithub.com/libp2p/go-libp2p-routing v0.1.0/go.mod h1:zfLhI1RI8RLEzmEaaPwzonRvXeeSHddONWkcTcB54nE=\ngithub.com/libp2p/go-libp2p-routing-helpers v0.2.3/go.mod h1:795bh+9YeoFl99rMASoiVgHdi5bjack0N1+AFAdbvBw=\ngithub.com/libp2p/go-libp2p-secio v0.1.0/go.mod h1:tMJo2w7h3+wN4pgU2LSYeiKPrfqBgkOsdiKK77hE7c8=\ngithub.com/libp2p/go-libp2p-secio v0.2.0/go.mod h1:2JdZepB8J5V9mBp79BmwsaPQhRPNN2NrnB2lKQcdy6g=\ngithub.com/libp2p/go-libp2p-secio v0.2.1/go.mod h1:cWtZpILJqkqrSkiYcDBh5lA3wbT2Q+hz3rJQq3iftD8=\ngithub.com/libp2p/go-libp2p-secio v0.2.2/go.mod h1:wP3bS+m5AUnFA+OFO7Er03uO1mncHG0uVwGrwvjYlNY=\ngithub.com/libp2p/go-libp2p-swarm v0.1.0/go.mod h1:wQVsCdjsuZoc730CgOvh5ox6K8evllckjebkdiY5ta4=\ngithub.com/libp2p/go-libp2p-swarm v0.2.2/go.mod h1:fvmtQ0T1nErXym1/aa1uJEyN7JzaTNyBcHImCxRpPKU=\ngithub.com/libp2p/go-libp2p-swarm v0.2.3/go.mod h1:P2VO/EpxRyDxtChXz/VPVXyTnszHvokHKRhfkEgFKNM=\ngithub.com/libp2p/go-libp2p-swarm v0.2.8/go.mod h1:JQKMGSth4SMqonruY0a8yjlPVIkb0mdNSwckW7OYziM=\ngithub.com/libp2p/go-libp2p-swarm v0.3.0/go.mod h1:hdv95GWCTmzkgeJpP+GK/9D9puJegb7H57B5hWQR5Kk=\ngithub.com/libp2p/go-libp2p-swarm v0.3.1/go.mod h1:hdv95GWCTmzkgeJpP+GK/9D9puJegb7H57B5hWQR5Kk=\ngithub.com/libp2p/go-libp2p-swarm v0.4.0 h1:hahq/ijRoeH6dgROOM8x7SeaKK5VgjjIr96vdrT+NUA=\ngithub.com/libp2p/go-libp2p-swarm v0.4.0/go.mod h1:XVFcO52VoLoo0eitSxNQWYq4D6sydGOweTOAjJNraCw=\ngithub.com/libp2p/go-libp2p-testing v0.0.2/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E=\ngithub.com/libp2p/go-libp2p-testing v0.0.3/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E=\ngithub.com/libp2p/go-libp2p-testing v0.0.4/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E=\ngithub.com/libp2p/go-libp2p-testing v0.1.0/go.mod h1:xaZWMJrPUM5GlDBxCeGUi7kI4eqnjVyavGroI2nxEM0=\ngithub.com/libp2p/go-libp2p-testing v0.1.1/go.mod h1:xaZWMJrPUM5GlDBxCeGUi7kI4eqnjVyavGroI2nxEM0=\ngithub.com/libp2p/go-libp2p-testing v0.1.2-0.20200422005655-8775583591d8/go.mod h1:Qy8sAncLKpwXtS2dSnDOP8ktexIAHKu+J+pnZOFZLTc=\ngithub.com/libp2p/go-libp2p-testing v0.3.0/go.mod h1:efZkql4UZ7OVsEfaxNHZPzIehtsBXMrXnCfJIgDti5g=\ngithub.com/libp2p/go-libp2p-testing v0.4.0 h1:PrwHRi0IGqOwVQWR3xzgigSlhlLfxgfXgkHxr77EghQ=\ngithub.com/libp2p/go-libp2p-testing v0.4.0/go.mod h1:Q+PFXYoiYFN5CAEG2w3gLPEzotlKsNSbKQ/lImlOWF0=\ngithub.com/libp2p/go-libp2p-tls v0.1.3 h1:twKMhMu44jQO+HgQK9X8NHO5HkeJu2QbhLzLJpa8oNM=\ngithub.com/libp2p/go-libp2p-tls v0.1.3/go.mod h1:wZfuewxOndz5RTnCAxFliGjvYSDA40sKitV4c50uI1M=\ngithub.com/libp2p/go-libp2p-transport-upgrader v0.1.1/go.mod h1:IEtA6or8JUbsV07qPW4r01GnTenLW4oi3lOPbUMGJJA=\ngithub.com/libp2p/go-libp2p-transport-upgrader v0.2.0/go.mod h1:mQcrHj4asu6ArfSoMuyojOdjx73Q47cYD7s5+gZOlns=\ngithub.com/libp2p/go-libp2p-transport-upgrader v0.3.0/go.mod h1:i+SKzbRnvXdVbU3D1dwydnTmKRPXiAR/fyvi1dXuL4o=\ngithub.com/libp2p/go-libp2p-transport-upgrader v0.4.0 h1:xwj4h3hJdBrxqMOyMUjwscjoVst0AASTsKtZiTChoHI=\ngithub.com/libp2p/go-libp2p-transport-upgrader v0.4.0/go.mod h1:J4ko0ObtZSmgn5BX5AmegP+dK3CSnU2lMCKsSq/EY0s=\ngithub.com/libp2p/go-libp2p-yamux v0.2.0/go.mod h1:Db2gU+XfLpm6E4rG5uGCFX6uXA8MEXOxFcRoXUODaK8=\ngithub.com/libp2p/go-libp2p-yamux v0.2.2/go.mod h1:lIohaR0pT6mOt0AZ0L2dFze9hds9Req3OfS+B+dv4qw=\ngithub.com/libp2p/go-libp2p-yamux v0.2.5/go.mod h1:Zpgj6arbyQrmZ3wxSZxfBmbdnWtbZ48OpsfmQVTErwA=\ngithub.com/libp2p/go-libp2p-yamux v0.2.7/go.mod h1:X28ENrBMU/nm4I3Nx4sZ4dgjZ6VhLEn0XhIoZ5viCwU=\ngithub.com/libp2p/go-libp2p-yamux v0.2.8/go.mod h1:/t6tDqeuZf0INZMTgd0WxIRbtK2EzI2h7HbFm9eAKI4=\ngithub.com/libp2p/go-libp2p-yamux v0.4.0/go.mod h1:+DWDjtFMzoAwYLVkNZftoucn7PelNoy5nm3tZ3/Zw30=\ngithub.com/libp2p/go-libp2p-yamux v0.5.0/go.mod h1:AyR8k5EzyM2QN9Bbdg6X1SkVVuqLwTGf0L4DFq9g6po=\ngithub.com/libp2p/go-libp2p-yamux v0.5.1 h1:sX4WQPHMhRxJE5UZTfjEuBvlQWXB5Bo3A2JK9ZJ9EM0=\ngithub.com/libp2p/go-libp2p-yamux v0.5.1/go.mod h1:dowuvDu8CRWmr0iqySMiSxK+W0iL5cMVO9S94Y6gkv4=\ngithub.com/libp2p/go-maddr-filter v0.0.4/go.mod h1:6eT12kSQMA9x2pvFQa+xesMKUBlj9VImZbj3B9FBH/Q=\ngithub.com/libp2p/go-maddr-filter v0.0.5/go.mod h1:Jk+36PMfIqCJhAnaASRH83bdAvfDRp/w6ENFaC9bG+M=\ngithub.com/libp2p/go-maddr-filter v0.1.0/go.mod h1:VzZhTXkMucEGGEOSKddrwGiOv0tUhgnKqNEmIAz/bPU=\ngithub.com/libp2p/go-mplex v0.0.3/go.mod h1:pK5yMLmOoBR1pNCqDlA2GQrdAVTMkqFalaTWe7l4Yd0=\ngithub.com/libp2p/go-mplex v0.1.0/go.mod h1:SXgmdki2kwCUlCCbfGLEgHjC4pFqhTp0ZoV6aiKgxDU=\ngithub.com/libp2p/go-mplex v0.1.1/go.mod h1:Xgz2RDCi3co0LeZfgjm4OgUF15+sVR8SRcu3SFXI1lk=\ngithub.com/libp2p/go-mplex v0.1.2/go.mod h1:Xgz2RDCi3co0LeZfgjm4OgUF15+sVR8SRcu3SFXI1lk=\ngithub.com/libp2p/go-mplex v0.2.0/go.mod h1:0Oy/A9PQlwBytDRp4wSkFnzHYDKcpLot35JQ6msjvYQ=\ngithub.com/libp2p/go-mplex v0.3.0 h1:U1T+vmCYJaEoDJPV1aq31N56hS+lJgb397GsylNSgrU=\ngithub.com/libp2p/go-mplex v0.3.0/go.mod h1:0Oy/A9PQlwBytDRp4wSkFnzHYDKcpLot35JQ6msjvYQ=\ngithub.com/libp2p/go-msgio v0.0.2/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ=\ngithub.com/libp2p/go-msgio v0.0.4/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ=\ngithub.com/libp2p/go-msgio v0.0.6 h1:lQ7Uc0kS1wb1EfRxO2Eir/RJoHkHn7t6o+EiwsYIKJA=\ngithub.com/libp2p/go-msgio v0.0.6/go.mod h1:4ecVB6d9f4BDSL5fqvPiC4A3KivjWn+Venn/1ALLMWA=\ngithub.com/libp2p/go-nat v0.0.4/go.mod h1:Nmw50VAvKuk38jUBcmNh6p9lUJLoODbJRvYAa/+KSDo=\ngithub.com/libp2p/go-nat v0.0.5 h1:qxnwkco8RLKqVh1NmjQ+tJ8p8khNLFxuElYG/TwqW4Q=\ngithub.com/libp2p/go-nat v0.0.5/go.mod h1:B7NxsVNPZmRLvMOwiEO1scOSyjA56zxYAGv1yQgRkEU=\ngithub.com/libp2p/go-netroute v0.1.2/go.mod h1:jZLDV+1PE8y5XxBySEBgbuVAXbhtuHSdmLPL2n9MKbk=\ngithub.com/libp2p/go-netroute v0.1.3 h1:1ngWRx61us/EpaKkdqkMjKk/ufr/JlIFYQAxV2XX8Ig=\ngithub.com/libp2p/go-netroute v0.1.3/go.mod h1:jZLDV+1PE8y5XxBySEBgbuVAXbhtuHSdmLPL2n9MKbk=\ngithub.com/libp2p/go-openssl v0.0.2/go.mod h1:v8Zw2ijCSWBQi8Pq5GAixw6DbFfa9u6VIYDXnvOXkc0=\ngithub.com/libp2p/go-openssl v0.0.3/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc=\ngithub.com/libp2p/go-openssl v0.0.4/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc=\ngithub.com/libp2p/go-openssl v0.0.5/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc=\ngithub.com/libp2p/go-openssl v0.0.7 h1:eCAzdLejcNVBzP/iZM9vqHnQm+XyCEbSSIheIPRGNsw=\ngithub.com/libp2p/go-openssl v0.0.7/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc=\ngithub.com/libp2p/go-reuseport v0.0.1/go.mod h1:jn6RmB1ufnQwl0Q1f+YxAj8isJgDCQzaaxIFYDhcYEA=\ngithub.com/libp2p/go-reuseport v0.0.2 h1:XSG94b1FJfGA01BUrT82imejHQyTxO4jEWqheyCXYvU=\ngithub.com/libp2p/go-reuseport v0.0.2/go.mod h1:SPD+5RwGC7rcnzngoYC86GjPzjSywuQyMVAheVBD9nQ=\ngithub.com/libp2p/go-reuseport-transport v0.0.2/go.mod h1:YkbSDrvjUVDL6b8XqriyA20obEtsW9BLkuOUyQAOCbs=\ngithub.com/libp2p/go-reuseport-transport v0.0.3/go.mod h1:Spv+MPft1exxARzP2Sruj2Wb5JSyHNncjf1Oi2dEbzM=\ngithub.com/libp2p/go-reuseport-transport v0.0.4 h1:OZGz0RB620QDGpv300n1zaOcKGGAoGVf8h9txtt/1uM=\ngithub.com/libp2p/go-reuseport-transport v0.0.4/go.mod h1:trPa7r/7TJK/d+0hdBLOCGvpQQVOU74OXbNCIMkufGw=\ngithub.com/libp2p/go-sockaddr v0.0.2 h1:tCuXfpA9rq7llM/v834RKc/Xvovy/AqM9kHvTV/jY/Q=\ngithub.com/libp2p/go-sockaddr v0.0.2/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k=\ngithub.com/libp2p/go-stream-muxer v0.0.1/go.mod h1:bAo8x7YkSpadMTbtTaxGVHWUQsR/l5MEaHbKaliuT14=\ngithub.com/libp2p/go-stream-muxer-multistream v0.2.0/go.mod h1:j9eyPol/LLRqT+GPLSxvimPhNph4sfYfMoDPd7HkzIc=\ngithub.com/libp2p/go-stream-muxer-multistream v0.3.0 h1:TqnSHPJEIqDEO7h1wZZ0p3DXdvDSiLHQidKKUGZtiOY=\ngithub.com/libp2p/go-stream-muxer-multistream v0.3.0/go.mod h1:yDh8abSIzmZtqtOt64gFJUXEryejzNb0lisTt+fAMJA=\ngithub.com/libp2p/go-tcp-transport v0.1.0/go.mod h1:oJ8I5VXryj493DEJ7OsBieu8fcg2nHGctwtInJVpipc=\ngithub.com/libp2p/go-tcp-transport v0.1.1/go.mod h1:3HzGvLbx6etZjnFlERyakbaYPdfjg2pWP97dFZworkY=\ngithub.com/libp2p/go-tcp-transport v0.2.0/go.mod h1:vX2U0CnWimU4h0SGSEsg++AzvBcroCGYw28kh94oLe0=\ngithub.com/libp2p/go-tcp-transport v0.2.1 h1:ExZiVQV+h+qL16fzCWtd1HSzPsqWottJ8KXwWaVi8Ns=\ngithub.com/libp2p/go-tcp-transport v0.2.1/go.mod h1:zskiJ70MEfWz2MKxvFB/Pv+tPIB1PpPUrHIWQ8aFw7M=\ngithub.com/libp2p/go-ws-transport v0.2.0/go.mod h1:9BHJz/4Q5A9ludYWKoGCFC5gUElzlHoKzu0yY9p/klM=\ngithub.com/libp2p/go-ws-transport v0.3.0/go.mod h1:bpgTJmRZAvVHrgHybCVyqoBmyLQ1fiZuEaBYusP5zsk=\ngithub.com/libp2p/go-ws-transport v0.3.1/go.mod h1:bpgTJmRZAvVHrgHybCVyqoBmyLQ1fiZuEaBYusP5zsk=\ngithub.com/libp2p/go-ws-transport v0.4.0 h1:9tvtQ9xbws6cA5LvqdE6Ne3vcmGB4f1z9SByggk4s0k=\ngithub.com/libp2p/go-ws-transport v0.4.0/go.mod h1:EcIEKqf/7GDjth6ksuS/6p7R49V4CBY6/E7R/iyhYUA=\ngithub.com/libp2p/go-yamux v1.2.2/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow=\ngithub.com/libp2p/go-yamux v1.3.0/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow=\ngithub.com/libp2p/go-yamux v1.3.3/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow=\ngithub.com/libp2p/go-yamux v1.3.5/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow=\ngithub.com/libp2p/go-yamux v1.3.7/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE=\ngithub.com/libp2p/go-yamux v1.4.0/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE=\ngithub.com/libp2p/go-yamux v1.4.1 h1:P1Fe9vF4th5JOxxgQvfbOHkrGqIZniTLf+ddhZp8YTI=\ngithub.com/libp2p/go-yamux v1.4.1/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE=\ngithub.com/libp2p/go-yamux/v2 v2.0.0 h1:vSGhAy5u6iHBq11ZDcyHH4Blcf9xlBhT4WQDoOE90LU=\ngithub.com/libp2p/go-yamux/v2 v2.0.0/go.mod h1:NVWira5+sVUIU6tu1JWvaRn1dRnG+cawOJiflsAM+7U=\ngithub.com/lucas-clemente/quic-go v0.19.3 h1:eCDQqvGBB+kCTkA0XrAFtNe81FMa0/fn4QSoeAbmiF4=\ngithub.com/lucas-clemente/quic-go v0.19.3/go.mod h1:ADXpNbTQjq1hIzCpB+y/k5iz4n4z4IwqoLb94Kh5Hu8=\ngithub.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=\ngithub.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=\ngithub.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc=\ngithub.com/marten-seemann/qtls v0.10.0 h1:ECsuYUKalRL240rRD4Ri33ISb7kAQ3qGDlrrl55b2pc=\ngithub.com/marten-seemann/qtls v0.10.0/go.mod h1:UvMd1oaYDACI99/oZUYLzMCkBXQVT0aGm99sJhbT8hs=\ngithub.com/marten-seemann/qtls-go1-15 v0.1.1 h1:LIH6K34bPVttyXnUWixk0bzH6/N07VxbSabxn5A5gZQ=\ngithub.com/marten-seemann/qtls-go1-15 v0.1.1/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I=\ngithub.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=\ngithub.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=\ngithub.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=\ngithub.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=\ngithub.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=\ngithub.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=\ngithub.com/miekg/dns v1.1.12/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=\ngithub.com/miekg/dns v1.1.28/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=\ngithub.com/miekg/dns v1.1.31/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=\ngithub.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 h1:lYpkrQH5ajf0OXOcUbGjvZxxijuBwbbmlSxLiuofa+g=\ngithub.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ=\ngithub.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U=\ngithub.com/minio/sha256-simd v0.0.0-20190328051042-05b4dd3047e5/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U=\ngithub.com/minio/sha256-simd v0.1.0/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U=\ngithub.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=\ngithub.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU=\ngithub.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=\ngithub.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=\ngithub.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8=\ngithub.com/mr-tron/base58 v1.1.1/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8=\ngithub.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=\ngithub.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=\ngithub.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=\ngithub.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=\ngithub.com/multiformats/go-base32 v0.0.3 h1:tw5+NhuwaOjJCC5Pp82QuXbrmLzWg7uxlMFp8Nq/kkI=\ngithub.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA=\ngithub.com/multiformats/go-base36 v0.1.0 h1:JR6TyF7JjGd3m6FbLU2cOxhC0Li8z8dLNGQ89tUg4F4=\ngithub.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM=\ngithub.com/multiformats/go-multiaddr v0.0.1/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44=\ngithub.com/multiformats/go-multiaddr v0.0.2/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44=\ngithub.com/multiformats/go-multiaddr v0.0.4/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44=\ngithub.com/multiformats/go-multiaddr v0.1.0/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44=\ngithub.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo=\ngithub.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y95ncPj0vFwVF6k6wJ4=\ngithub.com/multiformats/go-multiaddr v0.2.1/go.mod h1:s/Apk6IyxfvMjDafnhJgJ3/46z7tZ04iMk5wP4QMGGE=\ngithub.com/multiformats/go-multiaddr v0.2.2/go.mod h1:NtfXiOtHvghW9KojvtySjH5y0u0xW5UouOmQQrn6a3Y=\ngithub.com/multiformats/go-multiaddr v0.3.0/go.mod h1:dF9kph9wfJ+3VLAaeBqo9Of8x4fJxp6ggJGteB8HQTI=\ngithub.com/multiformats/go-multiaddr v0.3.1 h1:1bxa+W7j9wZKTZREySx1vPMs2TqrYWjVZ7zE6/XLG1I=\ngithub.com/multiformats/go-multiaddr v0.3.1/go.mod h1:uPbspcUPd5AfaP6ql3ujFY+QWzmBD8uLLL4bXW0XfGc=\ngithub.com/multiformats/go-multiaddr-dns v0.0.1/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q=\ngithub.com/multiformats/go-multiaddr-dns v0.0.2/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q=\ngithub.com/multiformats/go-multiaddr-dns v0.2.0 h1:YWJoIDwLePniH7OU5hBnDZV6SWuvJqJ0YtN6pLeH9zA=\ngithub.com/multiformats/go-multiaddr-dns v0.2.0/go.mod h1:TJ5pr5bBO7Y1B18djPuRsVkduhQH2YqYSbxWJzYGdK0=\ngithub.com/multiformats/go-multiaddr-fmt v0.0.1/go.mod h1:aBYjqL4T/7j4Qx+R73XSv/8JsgnRFlf0w2KGLCmXl3Q=\ngithub.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E=\ngithub.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo=\ngithub.com/multiformats/go-multiaddr-net v0.0.1/go.mod h1:nw6HSxNmCIQH27XPGBuX+d1tnvM7ihcFwHMSstNAVUU=\ngithub.com/multiformats/go-multiaddr-net v0.1.0/go.mod h1:5JNbcfBOP4dnhoZOv10JJVkJO0pCCEf8mTnipAo2UZQ=\ngithub.com/multiformats/go-multiaddr-net v0.1.1/go.mod h1:5JNbcfBOP4dnhoZOv10JJVkJO0pCCEf8mTnipAo2UZQ=\ngithub.com/multiformats/go-multiaddr-net v0.1.2/go.mod h1:QsWt3XK/3hwvNxZJp92iMQKME1qHfpYmyIjFVsSOY6Y=\ngithub.com/multiformats/go-multiaddr-net v0.1.3/go.mod h1:ilNnaM9HbmVFqsb/qcNysjCu4PVONlrBZpHIrw/qQuA=\ngithub.com/multiformats/go-multiaddr-net v0.1.4/go.mod h1:ilNnaM9HbmVFqsb/qcNysjCu4PVONlrBZpHIrw/qQuA=\ngithub.com/multiformats/go-multiaddr-net v0.1.5/go.mod h1:ilNnaM9HbmVFqsb/qcNysjCu4PVONlrBZpHIrw/qQuA=\ngithub.com/multiformats/go-multiaddr-net v0.2.0 h1:MSXRGN0mFymt6B1yo/6BPnIRpLPEnKgQNvVfCX5VDJk=\ngithub.com/multiformats/go-multiaddr-net v0.2.0/go.mod h1:gGdH3UXny6U3cKKYCvpXI5rnK7YaOIEOPVDI9tsJbEA=\ngithub.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs=\ngithub.com/multiformats/go-multibase v0.0.3 h1:l/B6bJDQjvQ5G52jw4QGSYeOTZoAwIO77RblWplfIqk=\ngithub.com/multiformats/go-multibase v0.0.3/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc=\ngithub.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U=\ngithub.com/multiformats/go-multihash v0.0.5/go.mod h1:lt/HCbqlQwlPBz7lv0sQCdtfcMtlJvakRUn/0Ual8po=\ngithub.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew=\ngithub.com/multiformats/go-multihash v0.0.9/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew=\ngithub.com/multiformats/go-multihash v0.0.10/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew=\ngithub.com/multiformats/go-multihash v0.0.13/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc=\ngithub.com/multiformats/go-multihash v0.0.14 h1:QoBceQYQQtNUuf6s7wHxnE2c8bhbMqhfGzNI032se/I=\ngithub.com/multiformats/go-multihash v0.0.14/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc=\ngithub.com/multiformats/go-multistream v0.1.0/go.mod h1:fJTiDfXJVmItycydCnNx4+wSzZ5NwG2FEVAI30fiovg=\ngithub.com/multiformats/go-multistream v0.1.1/go.mod h1:KmHZ40hzVxiaiwlj3MEbYgK9JFk2/9UktWZAF54Du38=\ngithub.com/multiformats/go-multistream v0.2.0 h1:6AuNmQVKUkRnddw2YiDjt5Elit40SFxMJkVnhmETXtU=\ngithub.com/multiformats/go-multistream v0.2.0/go.mod h1:5GZPQZbkWOLOn3J2y4Y99vVW7vOfsAflxARk3x14o6k=\ngithub.com/multiformats/go-varint v0.0.1/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=\ngithub.com/multiformats/go-varint v0.0.2/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=\ngithub.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=\ngithub.com/multiformats/go-varint v0.0.6 h1:gk85QWKxh3TazbLxED/NlDVv8+q+ReFJk7Y2W/KhfNY=\ngithub.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=\ngithub.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=\ngithub.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=\ngithub.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=\ngithub.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=\ngithub.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=\ngithub.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=\ngithub.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA=\ngithub.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=\ngithub.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=\ngithub.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=\ngithub.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=\ngithub.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=\ngithub.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=\ngithub.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=\ngithub.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=\ngithub.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=\ngithub.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=\ngithub.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=\ngithub.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=\ngithub.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=\ngithub.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys=\ngithub.com/peterbourgon/ff/v3 v3.0.0 h1:eQzEmNahuOjQXfuegsKQTSTDbf4dNvr/eNLrmJhiH7M=\ngithub.com/peterbourgon/ff/v3 v3.0.0/go.mod h1:UILIFjRH5a/ar8TjXYLTkIvSvekZqPm5Eb/qbGk6CT0=\ngithub.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=\ngithub.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=\ngithub.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=\ngithub.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=\ngithub.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=\ngithub.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=\ngithub.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=\ngithub.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=\ngithub.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0=\ngithub.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=\ngithub.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=\ngithub.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw=\ngithub.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI=\ngithub.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU=\ngithub.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag=\ngithub.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg=\ngithub.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw=\ngithub.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y=\ngithub.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=\ngithub.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q=\ngithub.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ=\ngithub.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I=\ngithub.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0=\ngithub.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ=\ngithub.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk=\ngithub.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=\ngithub.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=\ngithub.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=\ngithub.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=\ngithub.com/smola/gocompat v0.2.0/go.mod h1:1B0MlxbmoZNo3h8guHp8HztB3BSYR5itql9qtVc0ypY=\ngithub.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=\ngithub.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=\ngithub.com/spacemonkeygo/openssl v0.0.0-20181017203307-c2dcc5cca94a/go.mod h1:7AyxJNCJ7SBZ1MfVQCWD6Uqo2oubI2Eq2y2eqf+A5r0=\ngithub.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 h1:RC6RW7j+1+HkWaX/Yh71Ee5ZHaHYt7ZP4sQgUrm6cDU=\ngithub.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc=\ngithub.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=\ngithub.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=\ngithub.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=\ngithub.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=\ngithub.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=\ngithub.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=\ngithub.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=\ngithub.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=\ngithub.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=\ngithub.com/src-d/envconfig v1.0.0/go.mod h1:Q9YQZ7BKITldTBnoxsE5gOeB5y66RyPXeue/R4aaNBc=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=\ngithub.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=\ngithub.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=\ngithub.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=\ngithub.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=\ngithub.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=\ngithub.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=\ngithub.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 h1:EKhdznlJHPMoKr0XTrX+IlJs1LH3lyx2nfr1dOlZ79k=\ngithub.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1/go.mod h1:8UvriyWtv5Q5EOgjHaSseUEdkQfvwFv1I/In/O2M9gc=\ngithub.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc/go.mod h1:bopw91TMyo8J3tvftk8xmU2kPmlrt4nScJQZU2hE5EM=\ngithub.com/whyrusleeping/go-logging v0.0.1/go.mod h1:lDPYj54zutzG1XYfHAhcc7oNXEburHQBn+Iqd4yS4vE=\ngithub.com/whyrusleeping/mafmt v1.2.8/go.mod h1:faQJFPbLSxzD9xpA02ttW/tS9vZykNvXwGvqIpk20FA=\ngithub.com/whyrusleeping/mdns v0.0.0-20190826153040-b9b60ed33aa9/go.mod h1:j4l84WPFclQPj320J9gp0XwNKBb3U0zt5CBqjPp22G4=\ngithub.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 h1:E9S12nwJwEOXe2d6gT6qxdvqMnNq+VnSsKPgm2ZZNds=\ngithub.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7/go.mod h1:X2c0RVCI1eSUFI8eLcY3c0423ykwiUdxLJtkDvruhjI=\ngithub.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE=\ngithub.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=\ngo.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=\ngo.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=\ngo.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA=\ngo.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.4 h1:LYy1Hy3MJdrCdMwwzxA/dRok4ejH+RwNGbuoD9fCjto=\ngo.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=\ngo.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=\ngo.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=\ngo.uber.org/goleak v1.0.0 h1:qsup4IcBdlmsnGfqyLl4Ntn3C2XCCuKAE7DwHpScyUo=\ngo.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=\ngo.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=\ngo.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=\ngo.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=\ngo.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=\ngo.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=\ngo.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=\ngo.uber.org/zap v1.14.1/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=\ngo.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM=\ngo.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=\ngo.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM=\ngo.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=\ngo4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=\ngolang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=\ngolang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20190225124518-7f87c0fbb88b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM=\ngolang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=\ngolang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190227160552-c95aed5357e7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU=\ngolang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o=\ngolang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190526052359-791d8a0f4d09/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4=\ngolang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=\ngolang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\ngolang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20181130052023-1c3d964395ce/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191216052735-49a3e744a425 h1:VvQyQJN0tSuecqgcIxMWnnfG5kSmgy9KZR9sW3W5QeA=\ngolang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=\ngoogle.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=\ngoogle.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=\ngoogle.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=\ngoogle.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=\ngoogle.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=\ngoogle.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=\ngoogle.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=\ngoogle.golang.org/grpc v1.28.1/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=\ngoogle.golang.org/grpc v1.31.1 h1:SfXqXS5hkufcdZ/mHtYCh53P2b+92WQq/DZcKLgsFRs=\ngoogle.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=\ngoogle.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=\ngoogle.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=\ngoogle.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=\ngoogle.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=\ngoogle.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=\ngoogle.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=\ngopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=\ngopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=\ngopkg.in/src-d/go-cli.v0 v0.0.0-20181105080154-d492247bbc0d/go.mod h1:z+K8VcOYVYcSwSjGebuDL6176A1XskgbtNl64NSg+n8=\ngopkg.in/src-d/go-log.v1 v1.0.1/go.mod h1:GN34hKP0g305ysm2/hctJ0Y8nWP3zxXXJ8GFabTyABE=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=\ngopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=\ngopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngrpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=\nhonnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=\nhonnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=\nrsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=\nrsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=\nsourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=\nsourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=\n"
  },
  {
    "path": "tool/bench-cellular/server.go",
    "content": "package main\n\nimport (\n\t\"bufio\"\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"math/rand\"\n\t\"strconv\"\n\n\t\"github.com/libp2p/go-libp2p\"\n\tdht \"github.com/libp2p/go-libp2p-kad-dht\"\n\trouting \"github.com/libp2p/go-libp2p-routing\"\n\t\"github.com/libp2p/go-libp2p/core/event\"\n\t\"github.com/libp2p/go-libp2p/core/host\"\n\t\"github.com/libp2p/go-libp2p/core/network\"\n\t\"github.com/libp2p/go-libp2p/core/peer\"\n\tma \"github.com/multiformats/go-multiaddr\"\n\tmanet \"github.com/multiformats/go-multiaddr/net\"\n)\n\nvar tcpBertyRelays = []string{\n\t\"/ip4/51.159.21.214/tcp/4040/p2p/QmdT7AmhhnbuwvCpa5PH1ySK9HJVB82jr3fo1bxMxBPW6p\",\n\t\"/ip4/51.15.25.224/tcp/4040/p2p/12D3KooWHhDBv6DJJ4XDWjzEXq6sVNEs6VuxsV1WyBBEhPENHzcZ\",\n}\n\nvar quicBertyRelays = []string{\n\t\"/ip4/51.159.21.214/udp/4040/quic/p2p/QmdT7AmhhnbuwvCpa5PH1ySK9HJVB82jr3fo1bxMxBPW6p\",\n\t\"/ip4/51.15.25.224/udp/4040/quic/p2p/12D3KooWHhDBv6DJJ4XDWjzEXq6sVNEs6VuxsV1WyBBEhPENHzcZ\",\n}\n\nvar tcpIPFSRelays = []string{\n\t\"/ip4/147.75.80.110/tcp/4001/p2p/QmbFgm5zan8P6eWWmeyfncR5feYEMPbht5b1FW1C37aQ7y\",\n\t\"/ip4/147.75.195.153/tcp/4001/p2p/QmW9m57aiBDHAkKj9nmFSEn7ZqrcF1fZS4bipsTCHburei\",\n\t\"/ip4/147.75.70.221/tcp/4001/p2p/Qme8g49gm3q4Acp7xWBKg3nAa9fxZ1YmyDJdyGgoG6LsXh\",\n}\n\nvar quicIPFSRelays = []string{\n\t\"/ip4/147.75.80.110/udp/4001/quic/p2p/QmbFgm5zan8P6eWWmeyfncR5feYEMPbht5b1FW1C37aQ7y\",\n\t\"/ip4/147.75.195.153/udp/4001/quic/p2p/QmW9m57aiBDHAkKj9nmFSEn7ZqrcF1fZS4bipsTCHburei\",\n\t\"/ip4/147.75.70.221/udp/4001/quic/p2p/Qme8g49gm3q4Acp7xWBKg3nAa9fxZ1YmyDJdyGgoG6LsXh\",\n}\n\nconst (\n\tstaticBertyRelayMode = \"static-berty\"\n\tstaticIPFSRelayMode  = \"static-ipfs\"\n\tdiscoveryRelayMode   = \"discovery\"\n\tdisabledRelayMode    = \"none\"\n)\n\ntype serverOpts struct {\n\tport  int\n\tip6   bool\n\trelay string\n}\n\nfunc createServerHost(ctx context.Context, gOpts *globalOpts, sOpts *serverOpts) (host.Host, error) {\n\topts, err := globalOptsToLibp2pOpts(gOpts) // Get identity and transport\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif sOpts.relay == disabledRelayMode { // If no relay, add relevant listener\n\t\tif sOpts.ip6 {\n\t\t\tif gOpts.tcp {\n\t\t\t\topts = append(opts, libp2p.ListenAddrStrings(fmt.Sprintf(\"/ip6/::/tcp/%d\", sOpts.port)))\n\t\t\t} else {\n\t\t\t\topts = append(opts, libp2p.ListenAddrStrings(fmt.Sprintf(\"/ip6/::/udp/%d/quic\", sOpts.port)))\n\t\t\t}\n\t\t} else {\n\t\t\tif gOpts.tcp {\n\t\t\t\topts = append(opts, libp2p.ListenAddrStrings(fmt.Sprintf(\"/ip4/0.0.0.0/tcp/%d\", sOpts.port)))\n\t\t\t} else {\n\t\t\t\topts = append(opts, libp2p.ListenAddrStrings(fmt.Sprintf(\"/ip4/0.0.0.0/udp/%d/quic\", sOpts.port)))\n\t\t\t}\n\t\t}\n\t\topts = append(opts, libp2p.NATPortMap()) // Open port on NAT for access through public IP\n\t\topts = append(opts, libp2p.DisableRelay())\n\t} else {\n\t\topts = append(opts, libp2p.ListenAddrs()) // If using relay, set no listener\n\t\topts = append(opts, libp2p.EnableAutoRelay())\n\t\topts = append(opts, libp2p.ForceReachabilityPrivate())\n\t}\n\n\t// Relay discovery needs DHT routing to discover relays\n\t// NATPortMap (relay disabled) needs DHT routing to get host public IP\n\tif sOpts.relay == discoveryRelayMode || sOpts.relay == disabledRelayMode {\n\t\topts = append(\n\t\t\topts,\n\t\t\tlibp2p.Routing(func(h host.Host) (routing.PeerRouting, error) {\n\t\t\t\treturn dht.New(ctx, h, dht.Mode(dht.ModeClient), dht.BootstrapPeers(dht.GetDefaultBootstrapPeerAddrInfos()...))\n\t\t\t}),\n\t\t)\n\t} else { // Or setup Berty / IPFS static relays\n\t\tvar (\n\t\t\tmaddrRelays  []string\n\t\t\tstaticRelays []peer.AddrInfo\n\t\t)\n\n\t\tif sOpts.relay == staticBertyRelayMode {\n\t\t\tif gOpts.tcp {\n\t\t\t\tmaddrRelays = tcpBertyRelays\n\t\t\t} else {\n\t\t\t\tmaddrRelays = quicBertyRelays\n\t\t\t}\n\t\t} else {\n\t\t\tif gOpts.tcp {\n\t\t\t\tmaddrRelays = tcpIPFSRelays\n\t\t\t} else {\n\t\t\t\tmaddrRelays = quicIPFSRelays\n\t\t\t}\n\t\t}\n\n\t\tfor _, addr := range maddrRelays {\n\t\t\tmaddr, err := ma.NewMultiaddr(addr)\n\t\t\tif err != nil {\n\t\t\t\tlog.Printf(\"error: can't parse Multiaddr: %v\\n\", err)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tpi, err := peer.AddrInfoFromP2pAddr(maddr)\n\t\t\tif err != nil {\n\t\t\t\tlog.Printf(\"error: can't parse AddrInfo: %v\\n\", err)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tstaticRelays = append(staticRelays, *pi)\n\t\t}\n\n\t\topts = append(opts, libp2p.StaticRelays(staticRelays))\n\t}\n\n\treturn libp2p.New(ctx, opts...) // Create host\n}\n\nfunc printHint(h host.Host, gOpts *globalOpts, sOpts *serverOpts) {\n\tvar serverAddr ma.Multiaddr\n\n\tif sOpts.relay == disabledRelayMode {\n\t\tlog.Print(\"Waiting for public address...\")\n\t} else {\n\t\tlog.Print(\"Waiting for relay address...\")\n\t}\n\n\teventReceiver, err := h.EventBus().Subscribe(new(event.EvtLocalAddressesUpdated))\n\tif err != nil {\n\t\tlog.Fatalf(\"can't subscribe to local addresses updated events: %v\", err)\n\t}\n\tdefer eventReceiver.Close()\n\n\tfor ev := range eventReceiver.Out() {\n\t\tserverAddr = nil\n\t\tupdate := ev.(event.EvtLocalAddressesUpdated)\n\n\t\tfor _, addr := range update.Current {\n\t\t\tif addr.Action != event.Added {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif sOpts.relay != disabledRelayMode {\n\t\t\t\tif _, err := addr.Address.ValueForProtocol(ma.P_CIRCUIT); err != nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tif gOpts.tcp {\n\t\t\t\t\tif _, err := addr.Address.ValueForProtocol(ma.P_TCP); err != nil {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tserverAddr = addr.Address\n\t\t\t\t} else {\n\t\t\t\t\tif _, err := addr.Address.ValueForProtocol(ma.P_QUIC); err != nil {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tserverAddr = addr.Address\n\t\t\t\t}\n\t\t\t} else if sOpts.ip6 {\n\t\t\t\tif _, err := addr.Address.ValueForProtocol(ma.P_IP6); err != nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif manet.IsPublicAddr(addr.Address) {\n\t\t\t\t\tserverAddr = addr.Address\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif _, err := addr.Address.ValueForProtocol(ma.P_IP4); err != nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif manet.IsPublicAddr(addr.Address) {\n\t\t\t\t\tserverAddr = addr.Address\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif serverAddr != nil {\n\t\t\thostAddr, err := ma.NewMultiaddr(fmt.Sprintf(\"/ipfs/%s\", h.ID().String()))\n\t\t\tif err != nil {\n\t\t\t\tpanic(err)\n\t\t\t}\n\t\t\tfullAddr := serverAddr.Encapsulate(hostAddr)\n\n\t\t\thint := \"Now run: './bench\"\n\t\t\tif gOpts.insecure {\n\t\t\t\thint += \" -insecure\"\n\t\t\t}\n\t\t\tif gOpts.tcp {\n\t\t\t\thint += \" -tcp\"\n\t\t\t}\n\t\t\thint += fmt.Sprintf(\" client -dest %s [-request ...] [-size X] [-reco]'\", fullAddr)\n\t\t\tlog.Println(hint)\n\t\t}\n\t}\n}\n\nfunc runServer(ctx context.Context, gOpts *globalOpts, sOpts *serverOpts) error {\n\th, err := createServerHost(ctx, gOpts, sOpts)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"server host creation failed: %v\", err)\n\t}\n\n\tgo printHint(h, gOpts, sOpts)\n\n\th.SetStreamHandler(benchDownloadPID, func(s network.Stream) {\n\t\tdefer s.Close()\n\n\t\tremotePeerID := s.Conn().RemotePeer().String()\n\t\tlog.Printf(\"New download stream from: %s\\n\", remotePeerID)\n\n\t\t_, err := s.Write([]byte(\"\\n\"))\n\t\tif err != nil {\n\t\t\tlog.Printf(\"Write error during stream opened ack to %s: %v\\n\", remotePeerID, err)\n\t\t}\n\n\t\tbuf := bufio.NewReader(s)\n\t\tstr, err := buf.ReadString('\\n')\n\t\tif err != nil {\n\t\t\tlog.Printf(\"Read error during download from %s: %v\\n\", remotePeerID, err)\n\t\t\treturn\n\t\t}\n\n\t\tsize, err := strconv.Atoi(string(str[:len(str)-1]))\n\t\tif err != nil {\n\t\t\tlog.Printf(\"Invalid size received: %s: %v\", str, err)\n\t\t\treturn\n\t\t}\n\n\t\tdata := make([]byte, size)\n\t\trand.Read(data)\n\n\t\t_, err = s.Write(data)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"Write error during download to %s: %v\\n\", remotePeerID, err)\n\t\t}\n\t\tlog.Printf(\"Sent %d bytes to %s\", len(data), remotePeerID)\n\t})\n\n\th.SetStreamHandler(benchUploadPID, func(s network.Stream) {\n\t\tdefer s.Close()\n\n\t\tremotePeerID := s.Conn().RemotePeer().String()\n\t\tlog.Printf(\"New upload stream from: %s\\n\", remotePeerID)\n\n\t\t_, err := s.Write([]byte(\"\\n\"))\n\t\tif err != nil {\n\t\t\tlog.Printf(\"Write error during stream opened ack to %s: %v\\n\", remotePeerID, err)\n\t\t}\n\n\t\treader := bufio.NewReader(s)\n\t\tdata, err := io.ReadAll(reader)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"Read error during upload from %s: %v\\n\", remotePeerID, err)\n\t\t\treturn\n\t\t}\n\t\tlog.Printf(\"Received %d bytes from %s\", len(data), remotePeerID)\n\n\t\t_, err = s.Write([]byte(\"\\n\"))\n\t\tif err != nil {\n\t\t\tlog.Printf(\"Write error during uploaded ack to %s: %v\\n\", remotePeerID, err)\n\t\t}\n\t})\n\n\tselect {}\n}\n"
  },
  {
    "path": "tool/docker-protoc/Dockerfile",
    "content": "FROM    moul/protoc-gen-gotemplate:latest as pgg\n\n# build image\nFROM    golang:1.22-alpine as builder\n# install deps\nRUN     apk --no-cache add make git go rsync libc-dev openssh docker npm bash curl\n# ensure we use bash for all RUN commands\nSHELL [\"/bin/bash\", \"-c\"]\nRUN     git clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.12.0 && \\\n        echo -e '\\n. $HOME/.asdf/asdf.sh' >> ~/.bashrc\n# install compilers\nWORKDIR $GOPATH/src/berty.tech/weshnet\nCOPY    go.mod go.sum .tool-versions ./\n# ensure modifications to bashrc are properly sourced\nENV     BASH_ENV=~/.bashrc\n# @TODO(gfanton): use asdf version\nRUN     go install -mod=readonly \\\n          google.golang.org/protobuf/cmd/protoc-gen-go \\\n          github.com/srikrsna/protoc-gen-gotag \\\n          google.golang.org/grpc/cmd/protoc-gen-go-grpc \\\n          github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway \\\n          github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger \\\n          github.com/pseudomuto/protoc-gen-doc/cmd/protoc-gen-doc \\\n          golang.org/x/tools/cmd/goimports\nRUN     asdf plugin add buf && asdf install buf && \\\n        cp $(asdf which buf) /go/bin/buf\n\n# runtime\nFROM    golang:1.22-alpine\nRUN     apk --no-cache add git openssh make protobuf gcc libc-dev nodejs npm yarn sudo perl-utils tar sed grep \\\n &&     mkdir -p /.cache/go-build \\\n &&     chmod -R 777 /.cache \\\n &&     npm install -g eclint\nCOPY    --from=pgg     /go/bin/* /go/bin/\nCOPY    --from=builder /go/bin/* /go/bin/\nCOPY    --from=pgg     /protobuf /protobuf\nENV     GOPATH=/go \\\n        PATH=/go/bin:/node/node_modules/.bin:${PATH} \\\n        GOROOT=/usr/local/go\n"
  },
  {
    "path": "tool/docker-protoc/Makefile",
    "content": "IMAGE ?=\tbertytech/buf\nVERSION ?=\t5\n\nbuild:\n\tcd ../../ && docker build -f ./tool/docker-protoc/Dockerfile -t $(IMAGE):$(VERSION) -t $(IMAGE):latest .\n\npublish: build\n\tdocker push $(IMAGE):$(VERSION)\n\tdocker push $(IMAGE):latest\n"
  },
  {
    "path": "tyber.go",
    "content": "package weshnet\n\nconst (\n\tTyberEventTinderPeerFound  = \"Tinder peer found\"\n\tTyberEventTinderPeerJoined = \"Tinder peer joined\"\n\tTyberEventTinderPeerLeft   = \"Tinder peer left\"\n)\n"
  }
]