Repository: berty/weshnet Branch: main Commit: b4bab092a97a Files: 353 Total size: 2.3 MB Directory structure: gitextract_xdu0swom/ ├── .codecov.yml ├── .dockerignore ├── .editorconfig ├── .gitattributes ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_template.yml │ │ ├── feature_template.yml │ │ └── question_template.yml │ ├── dependabot.yml │ ├── pull_request_template.md │ ├── weekly-digest.yml │ └── workflows/ │ ├── benchmark.yml │ ├── buf-push.yml │ ├── cancel.yml │ ├── codeql-analysis.yml │ ├── dependent-issues.yml │ ├── go.yml │ ├── protobuf.yml │ ├── release.yml │ ├── ssh-runner.yml │ └── utils/ │ └── variables.json ├── .gitignore ├── .golangci.yml ├── .tool-versions ├── COPYRIGHT ├── INSTALL.md ├── LICENSE-APACHE ├── LICENSE-MIT ├── Makefile ├── README.md ├── account_export.go ├── account_export_test.go ├── api/ │ ├── go-internal/ │ │ ├── buf.yaml │ │ ├── handshake/ │ │ │ └── handshake.proto │ │ └── tinder/ │ │ └── records.proto │ └── protocol/ │ ├── buf.yaml │ ├── errcode/ │ │ └── errcode.proto │ ├── outofstoremessagetypes/ │ │ └── outofstoremessage.proto │ ├── protocoltypes.proto │ ├── replicationtypes/ │ │ └── bertyreplication.proto │ └── verifiablecredstypes/ │ └── bertyverifiablecreds.proto ├── api_app.go ├── api_client.go ├── api_contact.go ├── api_contact_request_test.go ├── api_contactrequest.go ├── api_debug.go ├── api_event.go ├── api_group.go ├── api_multimember.go ├── api_replication.go ├── api_verified_credentials.go ├── blackbox_test.go ├── buf.gen.tag.yaml ├── buf.gen.yaml ├── connectedness_manager.go ├── consts.go ├── contact_request_manager.go ├── contact_request_manager_test.go ├── deactivate_test.go ├── doc.go ├── docs/ │ ├── CONTRIBUTING.md │ ├── Makefile │ ├── apis/ │ │ ├── protocoltypes.md │ │ └── protocoltypes.swagger.json │ ├── architecture/ │ │ ├── 2020-11-27-adr-berty-grpc-bridge.txt │ │ ├── 2020-11-27-adr-gomobile-ipfs.md │ │ ├── README.md │ │ └── messenger-mvp/ │ │ ├── README.md │ │ └── contact-request.mermaid │ ├── buf-doc.gen.yaml │ ├── gen.sum │ ├── ideas/ │ │ ├── distributed-entropy.md │ │ └── entropy-pool.md │ └── protocol/ │ └── README.md ├── events.go ├── events_sig_checkers.go ├── gen.sum ├── go.mod ├── go.sum ├── group.go ├── group_context.go ├── iface_account.go ├── infra/ │ ├── .gitignore │ ├── README.md │ ├── rdvp/ │ │ ├── .env │ │ ├── Makefile │ │ └── docker-compose.yml │ └── relay/ │ ├── Dockerfile │ ├── Makefile │ ├── config.json │ └── docker-compose.yml ├── internal/ │ ├── benchmark/ │ │ └── benchmark_test.go │ ├── bertyversion/ │ │ ├── example_test.go │ │ └── version.go │ ├── datastoreutil/ │ │ ├── consts.go │ │ └── datastore_namespaced.go │ ├── handshake/ │ │ ├── doc.go │ │ ├── handshake.go │ │ ├── handshake.pb.go │ │ ├── handshake_test.go │ │ ├── handshake_util_test.go │ │ ├── request.go │ │ └── response.go │ ├── notify/ │ │ ├── notify.go │ │ └── notify_test.go │ ├── queue/ │ │ ├── metrics.go │ │ ├── priority.go │ │ ├── simple.go │ │ └── simple_test.go │ ├── sysutil/ │ │ ├── sysutil.go │ │ ├── sysutil_unix.go │ │ └── sysutil_unsupported.go │ └── tools/ │ ├── example_test.go │ ├── tools.go │ └── tools_untool.go ├── message_marshaler.go ├── message_marshaler_test.go ├── orbitdb.go ├── orbitdb_datastore_cache.go ├── orbitdb_many_adds_berty_test.go ├── orbitdb_many_adds_test.go ├── orbitdb_signed_entry_accesscontroller.go ├── orbitdb_signed_entry_identity_provider.go ├── orbitdb_signed_entry_keystore.go ├── orbitdb_test.go ├── orbitdb_utils_test.go ├── pkg/ │ ├── androidnearby/ │ │ ├── bridge_android.go │ │ ├── bridge_unsupported.go │ │ ├── const.go │ │ ├── example_test.go │ │ ├── init.go │ │ └── multiaddr.go │ ├── bertyvcissuer/ │ │ ├── client.go │ │ ├── urls.go │ │ └── verifiable_public_key_fetcher.go │ ├── ble-driver/ │ │ ├── BertyDevice_darwin.h │ │ ├── BertyDevice_darwin.m │ │ ├── BleInterface_darwin.h │ │ ├── BleInterface_darwin.m │ │ ├── BleManager_darwin.h │ │ ├── BleManager_darwin.m │ │ ├── BleQueue.h │ │ ├── BleQueue.m │ │ ├── CircularQueue.h │ │ ├── CircularQueue.m │ │ ├── ConnectedPeer.h │ │ ├── ConnectedPeer.m │ │ ├── CountDownLatch_darwin.h │ │ ├── CountDownLatch_darwin.m │ │ ├── Logger.h │ │ ├── Logger.m │ │ ├── PeerManager.h │ │ ├── PeerManager.m │ │ ├── TaskDelay.h │ │ ├── TaskDelay.m │ │ ├── WriteDataCache.h │ │ ├── WriteDataCache.m │ │ ├── bridge_android.go │ │ ├── bridge_darwin.go │ │ ├── bridge_unsupported.go │ │ ├── const.go │ │ ├── example_test.go │ │ ├── init.go │ │ └── multiaddr.go │ ├── cryptoutil/ │ │ ├── cryptoutil.go │ │ ├── cryptoutil_test.go │ │ ├── doc.go │ │ └── signer_wrapper.go │ ├── errcode/ │ │ ├── doc.go │ │ ├── errcode.pb.go │ │ ├── error.go │ │ ├── error_test.go │ │ └── stdproto.go │ ├── grpcutil/ │ │ ├── buf_listener.go │ │ ├── doc.go │ │ ├── simple_auth.go │ │ └── simple_auth_test.go │ ├── ipfsutil/ │ │ ├── collector_bandwidth.go │ │ ├── collector_host.go │ │ ├── conn_logger.go │ │ ├── conn_manager.go │ │ ├── consts.go │ │ ├── doc.go │ │ ├── extended_core_api.go │ │ ├── helpers.go │ │ ├── helpers_test.go │ │ ├── keystore_datastore.go │ │ ├── lifecycle.go │ │ ├── localrecord.go │ │ ├── metrics.go │ │ ├── mobile/ │ │ │ ├── host.go │ │ │ ├── node.go │ │ │ ├── repo.go │ │ │ └── routing.go │ │ ├── mobile.go │ │ ├── peering.go │ │ ├── pubsub_adaptater.go │ │ ├── pubsub_api.go │ │ ├── pubsub_monitor.go │ │ ├── repo.go │ │ ├── testing.go │ │ └── util.go │ ├── lifecycle/ │ │ ├── example_test.go │ │ ├── manager.go │ │ └── task.go │ ├── logutil/ │ │ ├── crypto_utils.go │ │ ├── encoders.go │ │ ├── example_test.go │ │ ├── file.go │ │ ├── file_test.go │ │ ├── grpc_logger.go │ │ ├── logger_native.go │ │ ├── logger_native_android.go │ │ ├── logger_native_darwin.go │ │ ├── logger_native_other.go │ │ ├── logutil.go │ │ ├── logutil_test.go │ │ ├── private_field.go │ │ └── stream.go │ ├── multipeer-connectivity-driver/ │ │ ├── bridge_darwin.go │ │ ├── bridge_unsupported.go │ │ ├── const.go │ │ ├── driver/ │ │ │ ├── Logger.h │ │ │ ├── Logger.m │ │ │ ├── MCManager.h │ │ │ ├── MCManager.m │ │ │ ├── cgo_bridge.go │ │ │ ├── mc-driver.h │ │ │ └── mc-driver.m │ │ ├── example_test.go │ │ ├── init.go │ │ └── multiaddr.go │ ├── netmanager/ │ │ ├── connectivity.go │ │ ├── netmanager.go │ │ ├── netmanager_noop.go │ │ └── netmanager_test.go │ ├── outofstoremessage/ │ │ ├── outofstoremessage_test.go │ │ └── service_outofstoremessage.go │ ├── outofstoremessagetypes/ │ │ ├── outofstoremessage.pb.go │ │ ├── outofstoremessage.pb.gw.go │ │ └── outofstoremessage_grpc.pb.go │ ├── protocoltypes/ │ │ ├── contact.go │ │ ├── doc.go │ │ ├── events_account.go │ │ ├── example_test.go │ │ ├── group.go │ │ ├── protocoltypes.pb.go │ │ ├── protocoltypes.pb.gw.go │ │ └── protocoltypes_grpc.pb.go │ ├── protoio/ │ │ ├── full.go │ │ ├── io.go │ │ ├── uint32.go │ │ └── varint.go │ ├── proximitytransport/ │ │ ├── addr.go │ │ ├── conn.go │ │ ├── example_test.go │ │ ├── listener.go │ │ ├── mplex.go │ │ ├── proximitydriver.go │ │ ├── ringBuffer_map.go │ │ └── transport.go │ ├── rendezvous/ │ │ ├── emitterio_sync_client.go │ │ ├── emitterio_sync_provider.go │ │ ├── emitterio_sync_test.go │ │ ├── rendezvous.go │ │ ├── rendezvous_test.go │ │ └── rotation.go │ ├── replicationtypes/ │ │ ├── bertyreplication.pb.go │ │ ├── bertyreplication.pb.gw.go │ │ ├── bertyreplication_grpc.pb.go │ │ ├── consts.go │ │ └── models.go │ ├── secretstore/ │ │ ├── chain_key.go │ │ ├── datastore_keys.go │ │ ├── device_keystore_wrapper.go │ │ ├── device_keystore_wrapper_test.go │ │ ├── doc.go │ │ ├── keys_utils.go │ │ ├── member_device.go │ │ ├── secret_store.go │ │ ├── secret_store_interfaces.go │ │ ├── secret_store_messages.go │ │ ├── secret_store_messages_test.go │ │ └── secret_store_test.go │ ├── testutil/ │ │ ├── doc.go │ │ ├── example_test.go │ │ ├── filters.go │ │ ├── logging.go │ │ ├── require.go │ │ ├── skip.go │ │ ├── skip_norace.go │ │ ├── skip_race.go │ │ └── skip_test.go │ ├── tinder/ │ │ ├── driver.go │ │ ├── driver_discovery.go │ │ ├── driver_localdiscovery.go │ │ ├── driver_localdiscovery_test.go │ │ ├── driver_mock.go │ │ ├── driver_mock_test.go │ │ ├── driver_rdvp.go │ │ ├── driver_service_test.go │ │ ├── filter.go │ │ ├── notify_network.go │ │ ├── options.go │ │ ├── peer_cache.go │ │ ├── peer_cache_test.go │ │ ├── records.pb.go │ │ ├── service.go │ │ ├── service_adaptater.go │ │ ├── service_advertises.go │ │ ├── service_mocked_test.go │ │ ├── service_subscription.go │ │ └── testing_test.go │ ├── tyber/ │ │ ├── context.go │ │ ├── format.go │ │ ├── ipfs.go │ │ ├── log.go │ │ ├── section.go │ │ ├── step.go │ │ └── subscribe.go │ ├── username/ │ │ ├── android.go │ │ ├── example_test.go │ │ ├── ios.go │ │ ├── others.go │ │ └── username.go │ └── verifiablecredstypes/ │ └── bertyverifiablecreds.pb.go ├── scenario_test.go ├── service.go ├── service_client.go ├── service_group.go ├── store_message.go ├── store_message_metrics.go ├── store_message_queue.go ├── store_message_test.go ├── store_metadata.go ├── store_metadata_index.go ├── store_metadata_test.go ├── store_options.go ├── store_utils.go ├── testing.go ├── testing_test.go ├── tinder_swiper.go ├── tinder_swiper_test.go ├── tool/ │ ├── bench-cellular/ │ │ ├── .gitignore │ │ ├── Makefile │ │ ├── bench.go │ │ ├── client.go │ │ ├── go.mod │ │ ├── go.sum │ │ └── server.go │ └── docker-protoc/ │ ├── Dockerfile │ └── Makefile └── tyber.go ================================================ FILE CONTENTS ================================================ ================================================ FILE: .codecov.yml ================================================ codecov: notify: require_ci_to_pass: yes coverage: precision: 2 round: down range: "1...100" status: project: no patch: no changes: no ignore: - "**/*.gen.go" - "**/generated.go" - "**/*.pb.go" - "**/*.pb.gw.go" - "**/gen/**" flags: go.unittests: carryforward: true js.unittests: carryforward: true ================================================ FILE: .dockerignore ================================================ *# *~ .#* .DS_Store .agignore .env .projectile Dockerfile core-sources.jar coverage.txt dist/ gen.sum.tmp gin-bin google-services.json js/ out/ profile.out vendor/ tool/ ================================================ FILE: .editorconfig ================================================ root = true [*] charset = utf-8 end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true indent_style = space indent_size = 4 [Makefile] indent_style = tab [*.go] indent_style = tab [*.proto] indent_size = 2 [*.swift] indent_size = 4 [*.tmpl] indent_size = 2 [*.{js,jsx,ts,tsx}] indent_size = 2 indent_style = tab block_comment_start = /* block_comment_end = */ [*.hbs] indent_size = 4 indent_style = tab block_comment_start = {{! block_comment_end = }} [*.html] indent_size = 2 [*.bat] end_of_line = crlf [*.{json,yml}] indent_size = 2 indent_style = space [.{babelrc,eslintrc}] indent_size = 2 [{Fastfile,.buckconfig,BUCK}] indent_size = 2 [*.diff] indent_size = 1 [*.m] indent_size = 1 indent_style = space block_comment_start = /** block_comment = * block_comment_end = */ [*.java] indent_size = 4 indent_style = space block_comment_start = /** block_comment = * block_comment_end = */ [js/packages/i18n/locale/**/*.{json,yml}] indent_size = 2 indent_style = tab ================================================ FILE: .gitattributes ================================================ # Auto detect text files and perform LF normalization * text=auto # Collapse vendored files on GitHub vendor/* linguist-vendored */vendor/* linguist-vendored client/common/openssl/built/* linguist-vendored # Collapse generated files on GitHub gen.sum linguist-generated merge=ours -diff *.gen.go linguist-generated merge=ours -diff *.gen.graphql linguist-generated merge=ours -diff *.gen.js linguist-generated merge=ours -diff *.gen.json linguist-generated merge=ours -diff *.gen.ts linguist-generated merge=ours -diff *.gen.tsx linguist-generated merge=ours -diff *.gen.yml linguist-generated merge=ours -diff *.pb.d.ts linguist-generated merge=ours -diff *.pb.go linguist-generated merge=ours -diff *.pb.gw.go linguist-generated merge=ours -diff *.pb.js linguist-generated merge=ours -diff *pb_test.go linguist-generated merge=ours -diff docs/apis/*types.md linguist-generated merge=ours -diff *.swagger.json linguist-generated merge=ours -diff api/*.yaml linguist-generated merge=ours -diff /js/packages/store/protocol/grpc-web-gen/** linguist-generated merge=ours -diff go.sum linguist-generated text yarn.lock linguist-generated text -diff Podfile.lock linguist-generated text -diff package.json linguist-generated go.mod text # Collapse snapshot files on GitHub *.snap linguist-generated # Reduce conflicts on markdown files *.md merge=union # specific for windows script files *.bat text eol=crlf ================================================ FILE: .github/ISSUE_TEMPLATE/bug_template.yml ================================================ # yamllint disable-line rule:document-start name: "Bug report" description: Report a bug found while using weshnet. labels: ["bug"] body: - type: checkboxes attributes: label: Is there an existing issue for this? description: >- Please search to see if an issue already exists for the bug you encountered. options: - label: I have searched the existing issues required: true - type: input id: package-version attributes: label: Package version description: What version of weshnet are you using? placeholder: v1.0.3 validations: required: true - type: dropdown id: os attributes: label: OS description: What OS are you seeing the problem on? options: - iOS - Android - Linux - macOS - Windows - Other - type: input id: language-version attributes: label: Language version and compiler version description: What programming language version and compiler are you using? placeholder: >- go1.18.4, javac 11.0.12 validations: required: true - type: textarea id: bug-description attributes: label: Bug description description: Provide a bug description and a code snippet if applicable. placeholder: | 1. Set up this environment ... 2. Add this code ... 3. Call this function ... validations: required: true - type: textarea id: current-behavior attributes: label: Current behavior description: >- Output after code execution including stack traces, debug logs, etc. placeholder: Currently ... validations: required: true - type: textarea id: expected-behavior attributes: label: Expected behavior description: Please provide what would be your expectation to happen. placeholder: In this situation, weshnet should ... validations: required: true - type: textarea id: environment attributes: label: Environment description: What is your development environment? placeholder: macOS 13.1 validations: required: true - type: textarea id: other attributes: label: Other placeholder: Any other details? ================================================ FILE: .github/ISSUE_TEMPLATE/feature_template.yml ================================================ # yamllint disable-line rule:document-start name: "Feature request" description: Suggest an idea for this project. labels: [":rocket: feature-request"] body: - type: checkboxes attributes: label: Is there an existing issue for this? description: >- Please search to see if an issue already exists for this feature request. options: - label: I have searched the existing issues required: true - type: textarea id: feature attributes: label: Feature request description: >- Provide a detailed description of the change or addition you are proposing. placeholder: There should be ... validations: required: true - type: textarea id: context attributes: label: Context description: >- Why is this change important to you? How would you use it? How can it benefit other users? placeholder: This feature request is important because ... validations: required: true - type: textarea id: implementation attributes: label: Possible implementation description: >- Not obligatory, but suggest an idea for implementing addition or change. placeholder: This feature could be implemented by ... ================================================ FILE: .github/ISSUE_TEMPLATE/question_template.yml ================================================ # yamllint disable-line rule:document-start name: "Question" description: Ask a question about this project. labels: ["question"] body: - type: checkboxes attributes: label: Asking a question, not reporting a bug description: >- If your question is "Why did I get this error?" and you think it is a bug, then please open a Bug Report at https://github.com/berty/weshnet/issues/new/ options: - label: This question is not about a bug required: true - type: checkboxes attributes: label: Is there an existing issue for this? description: >- Please search to see if an issue already exists for your question. options: - label: I have searched the existing issues required: true - type: textarea id: question attributes: label: Question description: >- Provide your question with enough detail that it is helpful to anyone reading the question (maybe years later). placeholder: How do I ... validations: required: true - type: textarea id: context attributes: label: Context description: >- Is it a general question about the design and goals of weshnet, or about how to use it in an app (or some other context)? placeholder: This is a question about ... validations: required: true ================================================ FILE: .github/dependabot.yml ================================================ version: 2 # @NOTE(gfanton): we use 0 as pull-request-limit to only enable security update. # see: https://docs.github.com/en/code-security/dependabot/dependabot-security-updates/configuring-dependabot-security-updates#overriding-the-default-behavior-with-a-configuration-file updates: - package-ecosystem: docker directory: "/tool/docker-protoc" schedule: interval: daily # Disable version updates for docker dependencies (enable only security update) open-pull-requests-limit: 0 labels: - "security" - "t/docker" - "dependencies" - package-ecosystem: github-actions directory: "/" schedule: interval: daily # Disable version updates for github dependencies (enable only security update) open-pull-requests-limit: 0 labels: - "security" - "t/github-actions" - "dependencies" - package-ecosystem: gomod directory: "/" schedule: interval: daily # Disable version updates for gomod dependencies (enable only security update) open-pull-requests-limit: 0 labels: - "security" - "t/golang" - "dependencies" ================================================ FILE: .github/pull_request_template.md ================================================ ================================================ FILE: .github/weekly-digest.yml ================================================ # Configuration for weekly-digest - https://github.com/apps/weekly-digest publishDay: thu canPublishIssues: true canPublishPullRequests: true canPublishContributors: true canPublishStargazers: true canPublishCommits: true ================================================ FILE: .github/workflows/benchmark.yml ================================================ name: Go benchmark on: push: tags: - v* branches: - main paths: - "**" - "!**.md" - "go.*" - "**.go" - ".github/workflows/benchmark.yml" pull_request: paths: - "**" - "!**.md" - "go.*" - "**.go" - ".github/workflows/benchmark.yml" jobs: benchmark: if: github.event_name == 'DISABLED' name: Run benchmarks runs-on: ubuntu-latest steps: - name: Setup asdf uses: asdf-vm/actions/setup@9cd779f40fe38688dd19505ccbc4eaaf018b44e7 with: asdf_version: 0.16.7 - name: Setup Graphviz uses: ts-graphviz/setup-graphviz@v1 - name: Checkout uses: actions/checkout@v3 with: fetch-depth: 50 # this is to make sure we obtain the target base commit# - name: Setup go run: | asdf plugin add golang asdf install golang echo "go_version=$(asdf current golang | xargs | cut -d ' ' -f 2)" >> $GITHUB_ENV go install golang.org/x/perf/cmd/benchstat@latest asdf reshim golang - name: Cache go modules uses: actions/cache@v4 with: path: ~/go/pkg/mod key: ${{ runner.os }}-go-${{ env.go_version }}-${{ env.json_cache-versions_go }}-${{ hashFiles('**/go.sum') }} restore-keys: ${{ runner.os }}-go-${{ env.go_version }}-${{ env.json_cache-versions_go }}- - name: Run benchmark run: make go.unittest | tee output_head.txt working-directory: . env: TEST_SPEED: any GO_TEST_PATH: ./internal/benchmark GO_TEST_OPTS: -bench=. -test.benchmem -cpuprofile cpu_head.prof -memprofile mem_head.prof -test.timeout=1200s -count=5 - name: Checkout base commit run: git checkout ${{ github.event.pull_request.base.sha }} if: github.event_name == 'pull_request' - name: Cache go modules (main) uses: actions/cache@v4 if: github.event_name == 'pull_request' with: path: ~/go/pkg/mod key: ${{ runner.os }}-go-${{ env.go_version }}-${{ env.json_cache-versions_go }}-${{ hashFiles('**/go.sum') }} restore-keys: ${{ runner.os }}-go-${{ env.go_version }}-${{ env.json_cache-versions_go }}- - name: Run benchmark (main) run: make go.unittest | tee output_base.txt if: github.event_name == 'pull_request' working-directory: . env: TEST_SPEED: any GO_TEST_PATH: ./internal/benchmark GO_TEST_OPTS: -bench=. -test.benchmem -cpuprofile cpu_base.prof -memprofile mem_base.prof -test.timeout=1200s -count=5 - name: Benchstat id: benchstat-main if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request' run: | echo 'Benchmark report' >> $GITHUB_STEP_SUMMARY echo '```' >> $GITHUB_STEP_SUMMARY benchstat output_head.txt >> $GITHUB_STEP_SUMMARY echo '```' >> $GITHUB_STEP_SUMMARY working-directory: . - name: Benchstat PR id: benchstat-pr if: github.event_name == 'pull_request' run: | echo 'Benchmark report' >> $GITHUB_STEP_SUMMARY echo '```' >> $GITHUB_STEP_SUMMARY benchstat output_head.txt output_base.txt >> $GITHUB_STEP_SUMMARY echo '```' >> $GITHUB_STEP_SUMMARY working-directory: . - name: Generate pprof html files id: pprof-html-files-no-relative working-directory: . run: | mkdir -p pprof_html/head/{cpu,mem}/{top,flamegraph,peek,source} 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 sleep 2 curl http://localhost:9402/ui/ > pprof_html/head/cpu/index.html curl http://localhost:9402/ui/top > pprof_html/head/cpu/top/index.html curl http://localhost:9402/ui/flamegraph > pprof_html/head/cpu/flamegraph/index.html curl http://localhost:9402/ui/peek > pprof_html/head/cpu/peek/index.html curl http://localhost:9402/ui/source > pprof_html/head/cpu/source/index.html pkill pprof sleep 2 go tool pprof -http 0.0.0.0:9402 -no_browser ./mem_head.prof < /dev/null & sleep 2 curl http://localhost:9402/ui/ > pprof_html/head/mem/index.html curl http://localhost:9402/ui/top > pprof_html/head/mem/top/index.html curl http://localhost:9402/ui/flamegraph > pprof_html/head/mem/flamegraph/index.html curl http://localhost:9402/ui/peek > pprof_html/head/mem/peek/index.html curl http://localhost:9402/ui/source > pprof_html/head/mem/source/index.html pkill pprof sleep 2 cat << EOF > pprof_html/index.html pprof output

CPU output

Memory output

EOF - name: Generate pprof html files (PR) if: github.event_name == 'pull_request' id: pprof-html-files-pr working-directory: . run: | mkdir -p pprof_html/base_comp/{cpu,mem} go tool pprof -http 0.0.0.0:9402 --diff_base=./cpu_base.prof -no_browser ./cpu_head.prof < /dev/null & sleep 2 curl http://localhost:9402/ui/ > pprof_html/base_comp/cpu/index.html pkill pprof sleep 2 go tool pprof -http 0.0.0.0:9402 --diff_base=./mem_base.prof -no_browser ./mem_head.prof < /dev/null & sleep 2 curl http://localhost:9402/ui/ > pprof_html/base_comp/mem/index.html pkill pprof sleep 2 cat << EOF >> pprof_html/index.html

CPU diff against ${{ github.event.pull_request.base.sha }}

Memory diff against ${{ github.event.pull_request.base.sha }}

EOF - name: Generate pprof html files (footer) id: pprof-html-files-footer working-directory: . run: | cat << EOF >> pprof_html/index.html EOF # upload arifacts # - name: upload artifact (main) uses: actions/upload-artifact@v3 if: github.ref == 'refs/heads/main' with: name: "bench-main" path: go/pprof_html - name: upload artifact (PR) uses: actions/upload-artifact@v3 if: github.event_name == 'pull_request' with: name: "bench-${{ github.event.pull_request.number }}" path: go/pprof_html ================================================ FILE: .github/workflows/buf-push.yml ================================================ name: buf-push on: push: branches: - main # from https://docs.buf.build/ci-cd/github-actions#buf-push jobs: buf-release: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: bufbuild/buf-setup-action@v1 # @TODO(gfanton): enable this ? # - uses: bufbuild/buf-lint-action@v1 # - uses: bufbuild/buf-breaking-action@v1 # with: # # The 'main' branch of the GitHub repository that defines the module. # against: "https://github.com/${GITHUB_REPOSITORY}.git#branch=main,ref=HEAD~1" - uses: bufbuild/buf-push-action@v1 with: input: "api/protocol" buf_token: ${{ secrets.BUF_TOKEN }} ================================================ FILE: .github/workflows/cancel.yml ================================================ name: Cancel on: workflow_run: workflows: [ "CodeQL", "Dependent Issues", "Go", "Integration", "macOS Release", "Protobuf", ] types: - requested jobs: cancel_pr_prev_push: name: Cancel previous runs on PR update if: ${{ github.event_name == 'workflow_run' }} runs-on: ubuntu-latest steps: - uses: styfle/cancel-workflow-action@0.11.0 with: workflow_id: ${{ github.event.workflow.id }} ================================================ FILE: .github/workflows/codeql-analysis.yml ================================================ # For most projects, this workflow file will not need changing; you simply need # to commit it to your repository. # # You may wish to alter this file to override the set of languages analyzed, # or to provide custom queries or build logic. # # ******** NOTE ******** # We have attempted to detect the languages in your repository. Please check # the `language` matrix defined below to confirm you have the correct set of # supported CodeQL languages. # name: "CodeQL" on: push: branches: [ main ] paths: - '**' - '!**.md' - 'go.*' - '**.go' - '.github/workflows/codeql-analysis.yml' pull_request: # The branches below must be a subset of the branches above branches: [ main ] paths: - '**' - '!**.md' - 'go.*' - '**.go' - '.github/workflows/codeql-analysis.yml' schedule: - cron: '28 12 * * 4' jobs: analyze: name: Analyze runs-on: ubuntu-latest strategy: fail-fast: false matrix: language: [ 'go' ] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] # Learn more: # 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 steps: - name: Checkout repository uses: actions/checkout@v3 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. # By default, queries listed here will override any specified in a config file. # Prefix the list here with "+" to use these queries and those in the config file. # queries: ./path/to/local/query, your-org/your-repo/queries@main # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild uses: github/codeql-action/autobuild@v2 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines # and modify them (or add more) to build your code if your project # uses a compiled language #- run: | # make bootstrap # make release - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v2 ================================================ FILE: .github/workflows/dependent-issues.yml ================================================ name: Dependent Issues on: issues: types: - opened - edited - reopened pull_request_target: types: - opened - edited - reopened - synchronize schedule: - cron: "42 2 * * *" # schedule daily check jobs: check: runs-on: ubuntu-latest steps: - uses: z0al/dependent-issues@v1.5.1 env: # (Required) The token to use to make API calls to GitHub. GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: # (Optional) The label to use to mark dependent issues label: dependent # (Optional) Enable checking for dependencies in issues. Enable by # setting the value to "on". Default "off" check_issues: off # (Optional) A comma-separated list of keywords. Default # "depends on, blocked by" keywords: depends on, blocked by ================================================ FILE: .github/workflows/go.yml ================================================ name: Go on: push: tags: - v* branches: - main paths: - "**" - "!**.md" - "go.*" - "**.go" - ".github/workflows/go.yml" pull_request: paths: - "**" - "!**.md" - "go.*" - "**.go" - ".github/workflows/go.yml" jobs: golangci-lint: name: Golangci-lint runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Setup asdf uses: asdf-vm/actions/setup@9cd779f40fe38688dd19505ccbc4eaaf018b44e7 with: asdf_version: 0.16.7 - name: Setup golang run: | asdf plugin add golang asdf install golang - name: Setup golangci-lint run: | asdf plugin add golangci-lint asdf install golangci-lint - name: Run golangci-lint run: make lint # this is not very common to have a job that checks the flappy tests. # # reason: some tests are flappy, they works, but not always; # this job checks that they are working sometimes. # if this job fails, then a test is "broken", not "flappy". # # summary: this job checks that "flappy tests" do not become "broken tests". # # we hope we can remove this job because all the tests are stable 100% of the time flappy-tests: name: Flappy tests (Linux) runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v3 - name: Load variables from file uses: antifree/json-to-variables@v1.0.1 with: filename: .github/workflows/utils/variables.json - name: Setup asdf uses: asdf-vm/actions/setup@9cd779f40fe38688dd19505ccbc4eaaf018b44e7 with: asdf_version: 0.16.7 - name: Setup go run: | asdf plugin add golang asdf install golang echo "go_version=$(asdf current golang | xargs | cut -d ' ' -f 2)" >> $GITHUB_ENV - name: Cache go modules uses: actions/cache@v4 with: path: ~/go/pkg/mod key: ${{ runner.os }}-go-${{ env.go_version }}-${{ env.json_cache-versions_go }}-${{ hashFiles('**/go.sum') }} restore-keys: ${{ runner.os }}-go-${{ env.go_version }}-${{ env.json_cache-versions_go }}- - name: Avoid triggering make generate run: touch gen.sum - name: Fetch go modules run: go mod download - name: Compile the testing binaries run: | pushd . && go test -c -o ./tests.bin . && popd - name: Check go.mod and go.sum run: | go mod tidy -v git --no-pager diff go.mod go.sum git --no-pager diff --quiet go.mod go.sum - name: Run fast flappy tests env: TEST_SPEED: fast TEST_STABILITY: flappy run: make go.flappy-tests go-tests-on-linux: name: Stable tests (Linux) runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v3 - name: Load variables from file uses: antifree/json-to-variables@v1.0.1 with: filename: .github/workflows/utils/variables.json - name: Setup asdf uses: asdf-vm/actions/setup@9cd779f40fe38688dd19505ccbc4eaaf018b44e7 with: asdf_version: 0.16.7 - name: Setup go run: | asdf plugin add golang asdf install golang echo "go_version=$(asdf current golang | xargs | cut -d ' ' -f 2)" >> $GITHUB_ENV - name: Cache go modules uses: actions/cache@v4 with: path: ~/go/pkg/mod key: ${{ runner.os }}-go-${{ env.go_version }}-${{ env.json_cache-versions_go }}-${{ hashFiles('**/go.sum') }} restore-keys: ${{ runner.os }}-go-${{ env.go_version }}-${{ env.json_cache-versions_go }}- - name: Check go.mod and go.sum run: | go mod tidy -v git --no-pager diff go.mod go.sum git --no-pager diff --quiet go.mod go.sum - name: Run fast tests multiple times env: TEST_SPEED: fast GO_TEST_OPTS: -test.timeout=600s -count 1 run: set -o pipefail; make go.unittest | tee test_log.txt - name: Run all tests env: TEST_SPEED: any GO_TEST_OPTS: -test.timeout=600s -count 1 run: make go.unittest - name: Run all tests with race flag and generate coverage env: TEST_SPEED: any GO_TEST_OPTS: -test.timeout=1200s -count=1 -race -cover -coverprofile=coverage.txt -covermode=atomic run: make go.unittest - name: Upload coverage to Codecov uses: codecov/codecov-action@v3.1.1 env: OS: ${{ runner.os }} GOLANG: ${{ env.go_version }} with: file: ./go/coverage.txt flags: go.unittests env_vars: OS,GOLANG name: codecov-umbrella fail_ci_if_error: false go-tests-on-windows: name: Stable tests (Windows) runs-on: windows-latest steps: - name: Checkout uses: actions/checkout@v3 - name: Load variables from file uses: antifree/json-to-variables@v1.0.1 with: filename: .github/workflows/utils/variables.json - name: Get go version shell: bash run: echo "go_version=$(cat .tool-versions | grep '^golang [0-9]\+\.[0-9]\+\.[0-9]\+.*$' | cut -d ' ' -f 2)" >> $GITHUB_ENV - name: Setup go uses: actions/setup-go@v3 with: go-version: ${{ env.go_version }} - name: Cache go modules uses: actions/cache@v4 with: path: ~/go/pkg/mod key: ${{ runner.os }}-go-${{ env.go_version }}-${{ env.json_cache-versions_go }}-${{ hashFiles('**/go.sum') }} restore-keys: ${{ runner.os }}-go-${{ env.go_version }}-${{ env.json_cache-versions_go }}- - name: Check go.mod and go.sum run: | go mod tidy -v git --no-pager diff go.mod go.sum git --no-pager diff --quiet go.mod go.sum - name: Run fast tests multiple times env: TEST_SPEED: fast run: go.exe test ./... -buildmode=exe -timeout=600s -count=5 - name: Run all tests env: TEST_SPEED: any run: go.exe test ./... -buildmode=exe -timeout=600s -count=1 go-tests-on-macos: name: Stable tests (macOS) runs-on: macos-latest steps: - name: Checkout uses: actions/checkout@v3 - name: Load variables from file uses: antifree/json-to-variables@v1.0.1 with: filename: .github/workflows/utils/variables.json - name: Setup asdf uses: asdf-vm/actions/setup@9cd779f40fe38688dd19505ccbc4eaaf018b44e7 with: asdf_version: 0.16.7 - name: Setup go run: | asdf plugin add golang asdf install golang echo "go_version=$(asdf current golang | xargs | cut -d ' ' -f 2)" >> $GITHUB_ENV - name: Cache go modules uses: actions/cache@v4 with: path: ~/go/pkg/mod key: ${{ runner.os }}-go-${{ env.go_version }}-${{ env.json_cache-versions_go }}-${{ hashFiles('**/go.sum') }} restore-keys: ${{ runner.os }}-go-${{ env.go_version }}-${{ env.json_cache-versions_go }}- - name: Check go.mod and go.sum run: | go mod tidy -v git --no-pager diff go.mod go.sum git --no-pager diff --quiet go.mod go.sum - name: Run fast tests multiple times env: TEST_SPEED: fast GO_TEST_OPTS: -test.timeout=600s -count 1 run: set -o pipefail; make go.unittest | tee test_log.txt - name: Run all tests env: TEST_SPEED: any GO_TEST_OPTS: -test.timeout=600s -count 1 run: make go.unittest - name: Run all tests with race flag and generate coverage env: TEST_SPEED: any GO_TEST_OPTS: -test.timeout=1200s -count=1 -race -cover -coverprofile=coverage.txt -covermode=atomic run: make go.unittest - name: Upload coverage to Codecov uses: codecov/codecov-action@v3.1.1 env: OS: ${{ runner.os }} GOLANG: ${{ env.go_version }} with: file: ./go/coverage.txt flags: go.unittests env_vars: OS,GOLANG name: codecov-umbrella fail_ci_if_error: false # TODO: consider adding various GOARCH check per OS. # i.e., to validate that we build on 32/64bit. ================================================ FILE: .github/workflows/protobuf.yml ================================================ name: Protobuf on: push: tags: - v* branches: - main paths: - "api/**" - "Makefile" - "docs/Makefile" - ".github/workflows/protobuf.yml" - "**/gen.sum" - "**.pb.go" - "**.gen.go" - "**.gen.graphql" - "**.gen.yml" - "**.pb.go" - "**/pb_test.go" - "**/docs/*/api.md" - "**/go.mod" - "**/go.sum" pull_request: paths: - "api/**" - "Makefile" - "docs/Makefile" - ".github/workflows/protobuf.yml" - "**/gen.sum" - "**.pb.go" - "**.gen.go" - "**.gen.graphql" - "**.gen.yml" - "**.pb.go" - "**/pb_test.go" - "**/docs/*/api.md" - "**/go.mod" - "**/go.sum" jobs: gen-go-and-docs: if: github.event_name == 'DISABLED' # need to fix it by removing docker for generation name: Generate go protobuf and docs runs-on: ubuntu-latest container: bertytech/buf:1 steps: - name: Checkout uses: actions/checkout@v3 - name: Unshallow run: git fetch --prune --unshallow - name: Remove lock files run: find . -name gen.sum -delete - name: Load variables from file uses: antifree/json-to-variables@v1.0.1 with: filename: .github/workflows/utils/variables.json - name: Setup asdf uses: asdf-vm/actions/setup@9cd779f40fe38688dd19505ccbc4eaaf018b44e7 with: asdf_version: 0.16.7 - name: Setup go run: | asdf plugin add golang asdf install golang echo "go_version=$(asdf current golang | xargs | cut -d ' ' -f 2)" >> $GITHUB_ENV - name: Setup jq run: | asdf plugin add jq asdf install jq - name: Cache go modules uses: actions/cache@v4 with: path: ~/go/pkg/mod key: ${{ runner.os }}-go-${{ env.go_version }}-${{ env.json_cache-versions_go }}-${{ hashFiles('**/go.sum') }} restore-keys: ${{ runner.os }}-go-${{ env.go_version }}-${{ env.json_cache-versions_go }}- - name: Fetch go modules run: go mod download - name: Generate docs working-directory: docs run: make generate_local - name: Generate go protobuf run: | make generate_local git checkout go.mod go.sum - name: Check diff run: | git status | cat git diff -w | cat git diff-index -w --quiet HEAD -- - name: Prepare openapi documentation working-directory: docs run: make openapi.prepare - name: Setup apiary run: apk --no-cache add ruby-dev g++ && gem install apiaryio - name: Upload API docs to apiary.io env: APIARY_API_KEY: "${{ secrets.APIARY_API_KEY }}" if: ${{ env.APIARY_API_KEY != 0 }} run: | apiary publish --api-name=bertyprotocol --path="docs/.tmp/openapi/bertyprotocol.swagger.json" || true ================================================ FILE: .github/workflows/release.yml ================================================ name: Release on: push: branches: - main pull_request: paths: # Go - "**" - "!**.md" - ".goreleaser" - "go.*" - "**.go" # CI - ".github/workflows/release.yml" jobs: semantic-release: name: Semantic release runs-on: ubuntu-latest outputs: new-release-published: ${{ steps.semantic-echo.outputs.new-release-published }} release-version: ${{ steps.semantic-echo.outputs.release-version }} steps: - name: Checkout uses: actions/checkout@v3 - name: Unshallow run: git fetch --prune --unshallow - name: Run Semantic Release id: semantic uses: docker://ghcr.io/codfish/semantic-release-action:v1 with: branches: | ['main'] plugins: | [ '@semantic-release/commit-analyzer', '@semantic-release/release-notes-generator', '@semantic-release/github' ] env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Export Semantic Release id: semantic-echo run: | echo "::set-output name=new-release-published::${{steps.semantic.outputs.new-release-published}}" echo "::set-output name=release-version::${{steps.semantic.outputs.release-version}}" post-semantic-release: needs: semantic-release #if: needs.semantic-release.outputs.new-release-published == 'true' runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v3 - name: Unshallow run: git fetch --prune --unshallow - name: Setup asdf uses: asdf-vm/actions/setup@9cd779f40fe38688dd19505ccbc4eaaf018b44e7 with: asdf_version: 0.16.7 - name: Setup go run: | asdf plugin add golang asdf install golang - name: Register version on pkg.go.dev if: needs.semantic-release.outputs.new-release-published == 'true' run: | package=$(cat go.mod | grep ^module | awk '{print $2}') version=v${{ needs.semantic-release.outputs.release-version }} url=https://proxy.golang.org/${package}/@v/${version}.info set -x +e curl -i $url ================================================ FILE: .github/workflows/ssh-runner.yml ================================================ name: SSH on runner on: workflow_dispatch: inputs: os: description: "Operating System" required: true default: ubuntu-latest type: choice options: - ubuntu-latest - macos-latest - windows-latest mod: description: "Install Go/Node modules" required: true default: true type: boolean jobs: setup-ssh: name: Setup runner and open SSH endpoint runs-on: ${{ github.event.inputs.os }} steps: - name: Checkout uses: actions/checkout@v3 with: fetch-depth: 0 persist-credentials: false - name: Load variables from file uses: antifree/json-to-variables@v1.0.1 with: filename: .github/workflows/utils/variables.json - name: Setup asdf uses: asdf-vm/actions/setup@9cd779f40fe38688dd19505ccbc4eaaf018b44e7 with: asdf_version: 0.16.7 - name: Setup go if: runner.os != 'Windows' run: | asdf plugin add golang asdf install golang echo "go_version=$(asdf current golang | xargs | cut -d ' ' -f 2)" >> $GITHUB_ENV - name: Cache go modules if: github.event.inputs.mod == 'true' && runner.os != 'Windows' uses: actions/cache@v4 with: path: ~/go/pkg/mod key: ${{ runner.os }}-go-${{ env.go_version }}-${{ env.json_cache-versions_go }}-${{ hashFiles('**/go.sum') }} restore-keys: ${{ runner.os }}-go-${{ env.go_version }}-${{ env.json_cache-versions_go }}- - name: Fetch go modules if: github.event.inputs.mod == 'true' && runner.os != 'Windows' working-directory: . run: go mod tidy - name: Install emacs shell: bash run: | if [ "$RUNNER_OS" == "Linux" ]; then sudo apt-get install -y emacs elif [ "$RUNNER_OS" == "Windows" ]; then choco install emacs else echo "Already installed!" fi - name: Setup tmate session uses: mxschmitt/action-tmate@v3 with: limit-access-to-actor: true ================================================ FILE: .github/workflows/utils/variables.json ================================================ { "cache-versions": { "go": "1" } } ================================================ FILE: .gitignore ================================================ *# *~ .#* .DS_Store .agignore .env .projectile .tmp/ .idea/ *.bin *.swp .vscode core-sources.jar coverage.txt dist/ gen.sum.tmp gin-bin out/ profile.out vendor/ debug.log benchmark_result.json # Delve's binaries *__debug_bin # Dev dbs /*gui.d ================================================ FILE: .golangci.yml ================================================ run: deadline: 1m tests: false issues: exclude-files: - ".*\\.pb\\.go$" - ".*\\.pb\\.gw\\.go$" - ".*\\.gen\\.go$" - "_test\\.go$" - "testing.go$" - ".*doc\\.go$" linters-settings: golint: min-confidence: 0 maligned: suggest-new: true misspell: locale: US gci: sections: - standard - default - prefix(berty.tech) # - prefix(github.com/libp2p) # - prefix(github.com/ipfs) linters: disable-all: true enable: - asciicheck - bodyclose #- depguard - dogsled - errcheck #- exhaustive # nice to have - exportloopref - gci - gochecknoinits #- gocognit # nice to have #- goconst - gocritic #- godot # nice to have #- goerr113 # nice to have - gofmt - gofumpt - goimports - revive #- gomnd # nice to have - gomodguard - gosec - gosimple - govet - ineffassign - misspell - nakedret - noctx #- nolintlint - exportloopref - staticcheck - typecheck - unconvert - unparam - unused - whitespace ================================================ FILE: .tool-versions ================================================ # To see the date when a version was updated, use git blame: # https://github.com/berty/weshnet/blame/main/.tool-versions #----- # This is simply the most recent version available to date of the lowest # major version of Go which is allowed by kubo. # There is no contraindication for updating it. #----- golang 1.22.5 #----- # This is simply the most recent golangci-lint version available to date. #----- golangci-lint 1.59.1 #----- # This is simply the most recent jq version available to date. # There is no contraindication for updating it. #----- jq 1.6 #----- # This is simply the most recent buf version available to date. # There is no contraindication for updating it. #----- buf 1.39.0 ================================================ FILE: COPYRIGHT ================================================ Copyright 2018-2023 Berty Technologies and other Berty Developers. Intellectual Property Notice ---------------------------- Berty is licensed under the Apache License, Version 2.0 (see LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) or the MIT license (see LICENSE-MIT or http://opensource.org/licenses/MIT), at your option. Copyrights and patents in the Berty project are retained by contributors. No copyright assignment is required to contribute to Berty SPDX-License-Identifier: (Apache-2.0 OR MIT) SPDX usage ---------- Individual files may contain SPDX tags instead of the full license text. This enables machine processing of license information based on the SPDX License Identifiers that are available here: https://spdx.org/licenses/ ================================================ FILE: INSTALL.md ================================================ # Build weshnet These are instructions to build weshnet. ## Prerequisites * Required: asdf * Required on macOS: Command Line Developer Tools Following are the steps to install each prerequisite (if it's needed for your build target). ### macOS 14, macOS 15 and macOS 26 To install the Command Line Developer Tools, in a terminal enter: xcode-select --install To install asdf using brew, follow instructions at https://asdf-vm.com . In short, first install brew following the instructions at https://brew.sh . Then, in a terminal enter: brew install asdf gpg If your terminal is zsh, enter: echo -e "\n. $(brew --prefix asdf)/libexec/asdf.sh" >> ${ZDOTDIR:-~}/.zshrc If your terminal is bash, enter: echo -e "\n. \"$(brew --prefix asdf)/libexec/asdf.sh\"" >> ~/.bash_profile Start a new terminal to get the changes to .zshrc . ### Ubuntu 18.04, 20.04, 22.04 and 24.04 To install asdf, follow instructions at https://asdf-vm.com . In short, in a terminal enter: sudo apt install curl git build-essential git clone https://github.com/asdf-vm/asdf.git ~/.asdf echo '. "$HOME/.asdf/asdf.sh"' >> ~/.bashrc Start a new terminal to get the changes to .bashrc . ## Build In a terminal, enter: git clone https://github.com/berty/weshnet cd weshnet First time only (or after updating .tool-versions), enter: make asdf.install_tools To run the tests, enter: make test Or you can make other targets. See: make help ================================================ FILE: LICENSE-APACHE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2018-2021 Berty Technologies Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: LICENSE-MIT ================================================ Copyright (c) 2018-2021 Berty Technologies Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: Makefile ================================================ ## ## Config ## GO ?= go GOPATH ?= $(HOME)/go GO_TAGS ?= -tags "" GO_TEST_OPTS ?= -test.timeout=300s -race -cover -coverprofile=coverage.txt -covermode=atomic $(GO_TAGS) GO_TEST_PATH ?= ./... GO_TEST_ENV ?= CI ?= false BUILD_DATE ?= `date +%s` VCS_REF ?= `git rev-parse --short HEAD` VERSION ?= `go run github.com/mdomke/git-semver/v5` LDFLAGS ?= -ldflags="-X berty.tech/weshnet/internal/bertyversion.VcsRef=$(VCS_REF) -X berty.tech/weshnet/internal/bertyversion.Version=$(VERSION)" # @FIXME(gfanton): on macOS Monterey (12.0.x) we currently need to set the # environment variable `MallocNanoZone` to 0 to avoid a SIGABRT or SIGSEGV # see https://github.com/golang/go/issues/49138 MACOS_VERSION=$(shell defaults read /System/Library/CoreServices/SystemVersion.plist 'ProductVersion' 2>/dev/null | sed 's/\.[0-9]$$//') ifeq ($(MACOS_VERSION),12.0) GO_TEST_ENV := MallocNanoZone=0 $(GO_TEST_ENV) endif ifeq ($(MACOS_VERSION),12.1) GO_TEST_ENV := MallocNanoZone=0 $(GO_TEST_ENV) endif ## ## General rules ## all: help .PHONY: all help: @echo "Available make commands:" @cat Makefile | grep '^[a-z]' | grep -v '=' | cut -d: -f1 | sort | sed 's/^/ /' .PHONY: help test: unittest lint tidy .PHONY: test unittest: go.unittest .PHONY: unittest generate: pb.generate docs.generate .PHONY: generate regenerate: gen.clean docs.clean generate docs.generate .PHONY: regenerate clean: gen.clean docs.clean rm -rf out/ .PHONY: clean re: clean generate .PHONY: re tidy: go.tidy .PHONY: tidy lint: go.lint .PHONY: lint lint.fix: go.fmt .PHONY: lint.fix ## ## Other rules ## check-program = $(foreach exec,$(1),$(if $(shell PATH="$(PATH)" which $(exec)),,$(error "No $(exec) in PATH"))) go.tidy: pb.generate $(call check-program, $(GO)) GO111MODULE=on $(GO) mod tidy .PHONY: go.tidy go.lint: pb.generate $(call check-program, golangci-lint) golangci-lint run --timeout=5m $(if $(filter $(CI), false), --verbose) ./... .PHONY: go.lint go.unittest: pb.generate $(call check-program, $(GO)) $(GO_TEST_ENV) GO111MODULE=on $(GO) test $(GO_TEST_OPTS) $(GO_TEST_PATH) .PHONY: go.unittest go.flappy-tests: pb.generate TEST_STABILITY=flappy go run moul.io/testman test -v -test.v -timeout=600s -retry=10 -run ^TestFlappy ./ TEST_STABILITY=flappy go run moul.io/testman test -v -test.v -timeout=600s -retry=10 -run ^TestScenario_ ./ TEST_STABILITY=flappy go run moul.io/testman test -v -test.v -timeout=600s -retry=10 -run ^TestFlappy ./pkg/tinder # FIXME: run on other packages too .PHONY: go.flappy-tests go.broken-tests: pb.generate TEST_STABILITY=broken go run moul.io/testman test -continue-on-error -timeout=1200s -test.timeout=60s -retry=5 -run ^TestScenario_ ./ .PHONY: go.broken-tests print-%: @echo $* = $($*) minimum_go_minor_version = 14 validate-go-version: @if [ ! "x`$(GO) version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f1`" = "x1" ]; then \ echo "error: Golang version should be \"1.x\". Please use 1.$(minimum_go_minor_version) or more recent."; \ exit 1; \ fi @if [ `$(GO) version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f2` -lt $(minimum_go_minor_version) ]; then \ echo "error: Golang version is not supported. Please use 1.$(minimum_go_minor_version) or more recent."; \ exit 1; \ fi .PHONY: validate-go-version ## ## Code gen ## protos_src := $(wildcard ../api/*.proto) $(wildcard ../api/go-internal/*.proto) gen_src := $(protos_src) Makefile gen_sum := gen.sum protoc_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 pb.generate: gen.sum validate-go-version $(gen_sum): $(gen_src) $(call check-program, shasum docker $(GO)) @shasum $(gen_src) | sort -k 2 > $(gen_sum).tmp @diff -q $(gen_sum).tmp $(gen_sum) || ( \ uid=`id -u`; \ set -xe; \ $(GO) mod download; \ docker run \ --user="$$uid" \ --volume="`go env GOPATH`/pkg/mod:/go/pkg/mod" \ --volume="$(PWD):/go/src/berty.tech/weshnet" \ --workdir="/go/src/berty.tech/weshnet" \ --entrypoint="sh" \ --rm \ bertytech/buf:5 \ -xec 'make generate_local'; \ $(MAKE) tidy \ ) .PHONY: pb.generate generate_local: go version $(call check-program, shasum buf) buf generate api/go-internal; buf generate api/protocol; buf generate --template buf.gen.tag.yaml api/go-internal; buf generate --template buf.gen.tag.yaml api/protocol; $(MAKE) go.fmt shasum $(gen_src) | sort -k 2 > $(gen_sum).tmp mv $(gen_sum).tmp $(gen_sum) .PHONY: generate_local go.fmt: go run github.com/daixiang0/gci write . \ --skip-generated -s 'standard,default,prefix(berty.tech)' go run mvdan.cc/gofumpt -w . .PHONY: go.fmt pkger.generate: $(GO) run github.com/markbates/pkger/cmd/pkger -o go/pkg/assets/ .PHONY: pkger.generate gen.clean: rm -f gen.sum $(wildcard */*/*.pb.go) $(wildcard */*/*pb_test.go) $(wildcard */*/*pb.gw.go) .PHONY: gen.clean pb.push: buf push api/protocol ## ## Docs gen ## docs.generate: cd docs; $(MAKE) generate .PHONY: docs.generate docs.clean: cd docs; $(MAKE) clean .PHONY: docs.generate asdf.install_plugins: $(call check-program, asdf) @echo "Installing asdf plugins..." @set -e; \ for PLUGIN in $$(cut -d' ' -f1 .tool-versions | grep "^[^\#]"); do \ asdf plugin add $$PLUGIN || [ $$?==2 ] || exit 1; \ done .PHONY: asdf.install_plugins asdf.install_tools: asdf.install_plugins $(call check-program, asdf) @echo "Installing asdf tools..." @asdf install .PHONY: asdf.install_tools ================================================ FILE: README.md ================================================ # Wesh Network Toolkit [![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white)](https://pkg.go.dev/berty.tech/weshnet) The Wesh network toolkit lets your application use the [Wesh protocol](https://berty.tech/docs/protocol) to support privacy-based, off-grid, peer-to-peer communication. Wesh powers [Berty Messenger](https://github.com/berty/berty#readme), and now you can use the Wesh network toolkit directly. Your application interfaces to Wesh based on [gRPC](https://grpc.io). So even though the core Wesh code is written in Go, Wesh works with your application written in Go, Python or [other languages](https://grpc.io/docs/languages) supported by gRPC. For details, see the Wesh website at https://wesh.network . The website includes [blog tutorials](https://wesh.network/blog) which introduce you to Wesh and walk you through some example applications and background of the Wesh protocol. ## Usage ```go import "berty.tech/weshnet" ``` Online API documentation is at https://buf.build/berty-technologies/weshnet . ## Get the code To get the code and build, see the file [INSTALL.md](https://github.com/berty/weshnet/blob/master/INSTALL.md). ## Feedback For bug reports, feature requests or questions, please open a [GitHub issue](https://github.com/berty/weshnet/issues/new/choose). ================================================ FILE: account_export.go ================================================ package weshnet import ( "archive/tar" "bytes" "context" "encoding/base64" "fmt" "io" "strings" "github.com/ipfs/go-cid" cbornode "github.com/ipfs/go-ipld-cbor" coreiface "github.com/ipfs/kubo/core/coreiface" mh "github.com/multiformats/go-multihash" "go.uber.org/multierr" "go.uber.org/zap" "google.golang.org/protobuf/proto" orbitdb "berty.tech/go-orbit-db" "berty.tech/weshnet/v2/pkg/errcode" "berty.tech/weshnet/v2/pkg/protocoltypes" ) const ( exportAccountKeyFilename = "account.key" exportAccountProofKeyFilename = "account_proof.key" exportOrbitDBEntriesPrefix = "entries/" exportOrbitDBHeadsPrefix = "heads/" ) func (s *service) export(ctx context.Context, output io.Writer) error { tw := tar.NewWriter(output) defer tw.Close() if err := s.exportAccountKeys(tw); err != nil { return errcode.ErrCode_ErrInternal.Wrap(err) } s.lock.RLock() groups := make([]*GroupContext, len(s.openedGroups)) i := 0 for _, gc := range s.openedGroups { groups[i] = gc i++ } s.lock.RUnlock() for _, gc := range groups { if err := s.exportGroupContext(ctx, gc, tw); err != nil { return errcode.ErrCode_ErrInternal.Wrap(err) } } return nil } func (s *service) exportGroupContext(ctx context.Context, gc *GroupContext, tw *tar.Writer) error { if err := s.exportOrbitDBStore(ctx, gc.metadataStore, tw); err != nil { return errcode.ErrCode_ErrInternal.Wrap(err) } if err := s.exportOrbitDBStore(ctx, gc.messageStore, tw); err != nil { return errcode.ErrCode_ErrInternal.Wrap(err) } metaRawHeads := gc.metadataStore.OpLog().RawHeads() cidsMeta := make([]cid.Cid, metaRawHeads.Len()) for i, raw := range metaRawHeads.Slice() { cidsMeta[i] = raw.GetHash() } messagesRawHeads := gc.messageStore.OpLog().RawHeads() cidsMessages := make([]cid.Cid, messagesRawHeads.Len()) for i, raw := range messagesRawHeads.Slice() { cidsMessages[i] = raw.GetHash() } if err := s.exportOrbitDBGroupHeads(gc, cidsMeta, cidsMessages, tw); err != nil { return errcode.ErrCode_ErrInternal.Wrap(err) } return nil } func (s *service) exportOrbitDBStore(ctx context.Context, store orbitdb.Store, tw *tar.Writer) error { allCIDs := store.OpLog().GetEntries().Keys() if len(allCIDs) == 0 { return nil } for _, idStr := range allCIDs { if err := s.exportOrbitDBEntry(ctx, tw, idStr); err != nil { if clErr := tw.Close(); clErr != nil { err = multierr.Append(err, clErr) } return errcode.ErrCode_ErrInternal.Wrap(err) } } return nil } func (s *service) exportAccountKeys(tw *tar.Writer) error { accountPrivateKeyBytes, accountProofPrivateKeyBytes, err := s.secretStore.ExportAccountKeysForBackup() if err != nil { return errcode.ErrCode_ErrInternal.Wrap(err) } err = exportPrivateKey(tw, accountPrivateKeyBytes, exportAccountKeyFilename) if err != nil { return errcode.ErrCode_ErrStreamWrite.Wrap(err) } err = exportPrivateKey(tw, accountProofPrivateKeyBytes, exportAccountProofKeyFilename) if err != nil { return errcode.ErrCode_ErrStreamWrite.Wrap(err) } return nil } func (s *service) exportOrbitDBGroupHeads(gc *GroupContext, headsMetadata []cid.Cid, headsMessages []cid.Cid, tw *tar.Writer) error { cidsMeta := make([][]byte, len(headsMetadata)) for i, id := range headsMetadata { cidsMeta[i] = id.Bytes() } cidsMessages := make([][]byte, len(headsMessages)) for i, id := range headsMessages { cidsMessages[i] = id.Bytes() } spk, err := gc.group.GetSigningPubKey() if err != nil { return errcode.ErrCode_ErrSerialization.Wrap(err) } spkBytes, err := spk.Raw() if err != nil { return errcode.ErrCode_ErrSerialization.Wrap(err) } linkKeyArr, err := gc.group.GetLinkKeyArray() if err != nil { return errcode.ErrCode_ErrSerialization.Wrap(err) } headsExport := &protocoltypes.GroupHeadsExport{ PublicKey: gc.group.PublicKey, SignPub: spkBytes, MetadataHeadsCids: cidsMeta, MessagesHeadsCids: cidsMessages, LinkKey: linkKeyArr[:], } entryName := base64.RawURLEncoding.EncodeToString(gc.group.PublicKey) data, err := proto.Marshal(headsExport) if err != nil { return errcode.ErrCode_ErrSerialization.Wrap(err) } if err := tw.WriteHeader(&tar.Header{ Typeflag: tar.TypeReg, Name: fmt.Sprintf("%s%s", exportOrbitDBHeadsPrefix, entryName), Mode: 0o600, Size: int64(len(data)), }); err != nil { return errcode.ErrCode_ErrStreamWrite.Wrap(err) } size, err := tw.Write(data) if err != nil { return errcode.ErrCode_ErrStreamWrite.Wrap(err) } if size != len(data) { return errcode.ErrCode_ErrStreamWrite.Wrap(fmt.Errorf("wrote %d bytes instead of %d", size, len(data))) } return nil } func exportPrivateKey(tw *tar.Writer, marshalledPrivateKey []byte, filename string) error { if err := tw.WriteHeader(&tar.Header{ Typeflag: tar.TypeReg, Name: filename, Mode: 0o600, Size: int64(len(marshalledPrivateKey)), }); err != nil { return errcode.ErrCode_ErrStreamWrite.Wrap(err) } size, err := tw.Write(marshalledPrivateKey) if err != nil { return errcode.ErrCode_ErrStreamWrite.Wrap(err) } if size != len(marshalledPrivateKey) { return errcode.ErrCode_ErrStreamWrite.Wrap(fmt.Errorf("wrote %d bytes instead of %d", size, len(marshalledPrivateKey))) } return nil } func (s *service) exportOrbitDBEntry(ctx context.Context, tw *tar.Writer, idStr string) error { id, err := cid.Parse(idStr) if err != nil { return errcode.ErrCode_ErrSerialization.Wrap(err) } dagNode, err := s.ipfsCoreAPI.Dag().Get(ctx, id) if err != nil { return errcode.ErrCode_ErrInternal.Wrap(err) } dagNodeBytes := dagNode.RawData() if err := tw.WriteHeader(&tar.Header{ Typeflag: tar.TypeReg, Name: fmt.Sprintf("%s%s", exportOrbitDBEntriesPrefix, idStr), Mode: 0o600, Size: int64(len(dagNodeBytes)), }); err != nil { return errcode.ErrCode_ErrStreamWrite.Wrap(err) } size, err := tw.Write(dagNodeBytes) if err != nil { return errcode.ErrCode_ErrStreamWrite.Wrap(err) } if size != len(dagNodeBytes) { return errcode.ErrCode_ErrStreamWrite.Wrap(fmt.Errorf("wrote %d bytes instead of %d", size, len(dagNodeBytes))) } return nil } func readExportSecretKeyFile(expectedSize int64, reader *tar.Reader) ([]byte, error) { if expectedSize == 0 { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("invalid expected key size")) } keyContents := new(bytes.Buffer) size, err := io.Copy(keyContents, reader) if err != nil { return nil, errcode.ErrCode_ErrInternal.Wrap(fmt.Errorf("unable to read %d bytes: %w", expectedSize, err)) } if size != expectedSize { return nil, errcode.ErrCode_ErrInternal.Wrap(fmt.Errorf("unexpected file size")) } return keyContents.Bytes(), nil } func readExportOrbitDBGroupHeads(expectedSize int64, reader *tar.Reader) (*protocoltypes.GroupHeadsExport, []cid.Cid, []cid.Cid, error) { if expectedSize == 0 { return nil, nil, nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("invalid expected node size")) } nodeContents := new(bytes.Buffer) size, err := io.Copy(nodeContents, reader) if err != nil { return nil, nil, nil, errcode.ErrCode_ErrInternal.Wrap(fmt.Errorf("unable to read %d bytes: %w", expectedSize, err)) } if size != expectedSize { return nil, nil, nil, errcode.ErrCode_ErrInternal.Wrap(fmt.Errorf("unexpected file size")) } groupHeads := &protocoltypes.GroupHeadsExport{} if err := proto.Unmarshal(nodeContents.Bytes(), groupHeads); err != nil { return nil, nil, nil, errcode.ErrCode_ErrDeserialization.Wrap(err) } messagesCIDs := make([]cid.Cid, len(groupHeads.MessagesHeadsCids)) for i, cidBytes := range groupHeads.MessagesHeadsCids { messagesCIDs[i], err = cid.Parse(cidBytes) if err != nil { return nil, nil, nil, errcode.ErrCode_ErrDeserialization.Wrap(err) } } metaCIDs := make([]cid.Cid, len(groupHeads.MetadataHeadsCids)) for i, cidBytes := range groupHeads.MetadataHeadsCids { metaCIDs[i], err = cid.Parse(cidBytes) if err != nil { return nil, nil, nil, errcode.ErrCode_ErrDeserialization.Wrap(err) } } return groupHeads, metaCIDs, messagesCIDs, nil } func readExportCBORNode(expectedSize int64, cidStr string, reader *tar.Reader) (*cbornode.Node, error) { if expectedSize == 0 { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("invalid expected node size")) } nodeContents := new(bytes.Buffer) expectedCID, err := cid.Parse(cidStr) if err != nil { return nil, errcode.ErrCode_ErrDeserialization.Wrap(fmt.Errorf("unable to parse CID in filename")) } size, err := io.Copy(nodeContents, reader) if err != nil { return nil, errcode.ErrCode_ErrInternal.Wrap(fmt.Errorf("unable to read %d bytes: %w", expectedSize, err)) } if size != expectedSize { return nil, errcode.ErrCode_ErrInternal.Wrap(fmt.Errorf("unexpected file size")) } node, err := cbornode.Decode(nodeContents.Bytes(), mh.SHA2_256, -1) if err != nil { return nil, errcode.ErrCode_ErrDeserialization.Wrap(err) } if !node.Cid().Equals(expectedCID) { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("entry CID doesn't match file CID")) } return node, nil } type RestoreAccountHandler struct { Handler func(header *tar.Header, reader *tar.Reader) (bool, error) PostProcess func() error } type restoreAccountState struct { keys map[string][]byte } func (state *restoreAccountState) readKey(keyName string) RestoreAccountHandler { return RestoreAccountHandler{ Handler: func(header *tar.Header, reader *tar.Reader) (bool, error) { if header.Name != keyName { return false, nil } if state.keys[keyName] != nil { return false, errcode.ErrCode_ErrInternal.Wrap(fmt.Errorf("multiple keys found in archive")) } var err error state.keys[keyName], err = readExportSecretKeyFile(header.Size, reader) if err != nil { return true, errcode.ErrCode_ErrInternal.Wrap(err) } return true, nil }, } } func (state *restoreAccountState) restoreKeys(odb *WeshOrbitDB) RestoreAccountHandler { return RestoreAccountHandler{ PostProcess: func() error { if err := odb.secretStore.ImportAccountKeys(state.keys[exportAccountKeyFilename], state.keys[exportAccountProofKeyFilename]); err != nil { return errcode.ErrCode_ErrInternal.Wrap(err) } return nil }, } } func restoreOrbitDBEntry(ctx context.Context, coreAPI coreiface.CoreAPI) RestoreAccountHandler { return RestoreAccountHandler{ Handler: func(header *tar.Header, reader *tar.Reader) (bool, error) { if !strings.HasPrefix(header.Name, exportOrbitDBEntriesPrefix) { return false, nil } cidStr := strings.TrimPrefix(header.Name, exportOrbitDBEntriesPrefix) node, err := readExportCBORNode(header.Size, cidStr, reader) if err != nil { return true, errcode.ErrCode_ErrInternal.Wrap(err) } if err := coreAPI.Dag().Add(ctx, node); err != nil { return true, errcode.ErrCode_ErrInternal.Wrap(err) } return true, nil }, } } func restoreOrbitDBHeads(ctx context.Context, odb *WeshOrbitDB) RestoreAccountHandler { return RestoreAccountHandler{ Handler: func(header *tar.Header, reader *tar.Reader) (bool, error) { if !strings.HasPrefix(header.Name, exportOrbitDBHeadsPrefix) { return false, nil } heads, metaCIDs, messageCIDs, err := readExportOrbitDBGroupHeads(header.Size, reader) if err != nil { return true, errcode.ErrCode_ErrInternal.Wrap(err) } if err := odb.setHeadsForGroup(ctx, &protocoltypes.Group{ PublicKey: heads.PublicKey, SignPub: heads.SignPub, LinkKey: heads.LinkKey, }, metaCIDs, messageCIDs); err != nil { return true, errcode.ErrCode_ErrOrbitDBAppend.Wrap(fmt.Errorf("error while restoring db head: %w", err)) } return true, nil }, } } func RestoreAccountExport(ctx context.Context, reader io.Reader, coreAPI coreiface.CoreAPI, odb *WeshOrbitDB, logger *zap.Logger, handlers ...RestoreAccountHandler) error { tr := tar.NewReader(reader) state := restoreAccountState{ keys: map[string][]byte{}, } handlers = append( []RestoreAccountHandler{ state.readKey(exportAccountKeyFilename), state.readKey(exportAccountProofKeyFilename), state.restoreKeys(odb), restoreOrbitDBEntry(ctx, coreAPI), restoreOrbitDBHeads(ctx, odb), }, handlers..., ) for { header, err := tr.Next() if err == io.EOF { break } else if err != nil { return errcode.ErrCode_ErrInternal.Wrap(err) } if header.Typeflag != tar.TypeReg { logger.Warn("invalid entry type", zap.String("filename", header.Name), zap.Any("filename", header.Typeflag)) continue } notHandled := true for _, h := range handlers { if h.Handler == nil { continue } handled, err := h.Handler(header, tr) if err != nil { return errcode.ErrCode_ErrInternal.Wrap(err) } if handled { notHandled = false break } } if notHandled { logger.Warn("unknown export entry", zap.String("filename", header.Name)) } } for _, h := range handlers { if h.PostProcess == nil { continue } if err := h.PostProcess(); err != nil { return errcode.ErrCode_ErrInternal.Wrap(err) } } return nil } ================================================ FILE: account_export_test.go ================================================ package weshnet import ( "archive/tar" "context" "io" "os" "testing" "github.com/ipfs/go-cid" ds "github.com/ipfs/go-datastore" dsync "github.com/ipfs/go-datastore/sync" mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" "github.com/stretchr/testify/require" orbitdb "berty.tech/go-orbit-db" "berty.tech/go-orbit-db/pubsub/pubsubraw" "berty.tech/weshnet/v2/pkg/ipfsutil" "berty.tech/weshnet/v2/pkg/protocoltypes" "berty.tech/weshnet/v2/pkg/secretstore" "berty.tech/weshnet/v2/pkg/testutil" "berty.tech/weshnet/v2/pkg/tinder" ) func Test_service_exportAccountKeys(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() mn := mocknet.New() defer mn.Close() msrv := tinder.NewMockDriverServer() dsA := dsync.MutexWrap(ds.NewMapDatastore()) nodeA, closeNodeA := NewTestingProtocol(ctx, t, &TestingOpts{ Mocknet: mn, DiscoveryServer: msrv, }, dsA) defer closeNodeA() // time.Sleep(time.Second * 5) s, ok := nodeA.Service.(*service) require.True(t, ok) tmpFile, err := os.CreateTemp(os.TempDir(), "test-export-") require.NoError(t, err) defer os.Remove(tmpFile.Name()) tw := tar.NewWriter(tmpFile) err = s.exportAccountKeys(tw) require.NoError(t, err) err = tw.Close() require.NoError(t, err) _, err = tmpFile.Seek(0, io.SeekStart) require.NoError(t, err) tr := tar.NewReader(tmpFile) accountPrivateKey := getKeyFromTar(t, tr, exportAccountKeyFilename) accountProofPrivateKey := getKeyFromTar(t, tr, exportAccountProofKeyFilename) inStoreAccountPrivateKeyBytes, inStoreAccountProofPrivateKeyBytes, err := s.secretStore.ExportAccountKeysForBackup() require.NoError(t, err) require.NotNil(t, inStoreAccountPrivateKeyBytes) require.NotNil(t, inStoreAccountProofPrivateKeyBytes) require.Equal(t, accountPrivateKey, inStoreAccountPrivateKeyBytes) require.Equal(t, accountProofPrivateKey, inStoreAccountProofPrivateKeyBytes) } func getKeyFromTar(t *testing.T, tr *tar.Reader, expectedFilename string) []byte { header, err := tr.Next() require.NoError(t, err) require.Equal(t, expectedFilename, header.Name) keyContents := make([]byte, header.Size) size, err := tr.Read(keyContents) require.Equal(t, int(header.Size), size) return keyContents } func TestFlappyRestoreAccount(t *testing.T) { testutil.FilterStability(t, testutil.Flappy) ctx, cancel := context.WithCancel(context.Background()) defer cancel() logger, cleanup := testutil.Logger(t) defer cleanup() mn := mocknet.New() defer mn.Close() msrv := tinder.NewMockDriverServer() tmpFile, err := os.CreateTemp(os.TempDir(), "test-export-") require.NoError(t, err) expectedMessages := map[cid.Cid][]byte{} var nodeAInstanceConfig *protocoltypes.ServiceGetConfiguration_Reply g, _, err := NewGroupMultiMember() require.NoError(t, err) defer os.Remove(tmpFile.Name()) { dsA := dsync.MutexWrap(ds.NewMapDatastore()) nodeA, closeNodeA := NewTestingProtocol(ctx, t, &TestingOpts{ Mocknet: mn, }, dsA) serviceA, ok := nodeA.Service.(*service) require.True(t, ok) nodeAInstanceConfig, err = nodeA.Client.ServiceGetConfiguration(ctx, &protocoltypes.ServiceGetConfiguration_Request{}) require.NoError(t, err) require.NotNil(t, nodeAInstanceConfig) testPayload1 := []byte("testMessage1") testPayload2 := []byte("testMessage2") testPayload3 := []byte("testMessage3") testPayload4 := []byte("testMessage4") accountGroup := serviceA.getAccountGroup() require.NotNil(t, accountGroup) op, err := accountGroup.messageStore.AddMessage(ctx, testPayload1) require.NoError(t, err) expectedMessages[op.GetEntry().GetHash()] = testPayload1 op, err = accountGroup.messageStore.AddMessage(ctx, testPayload2) require.NoError(t, err) expectedMessages[op.GetEntry().GetHash()] = testPayload2 _, err = nodeA.Client.MultiMemberGroupJoin(ctx, &protocoltypes.MultiMemberGroupJoin_Request{Group: g}) require.NoError(t, err) _, err = nodeA.Client.ActivateGroup(ctx, &protocoltypes.ActivateGroup_Request{GroupPk: g.PublicKey}) require.NoError(t, err) op, err = serviceA.openedGroups[string(g.PublicKey)].messageStore.AddMessage(ctx, testPayload3) require.NoError(t, err) expectedMessages[op.GetEntry().GetHash()] = testPayload3 op, err = serviceA.openedGroups[string(g.PublicKey)].messageStore.AddMessage(ctx, testPayload4) require.NoError(t, err) expectedMessages[op.GetEntry().GetHash()] = testPayload4 require.NoError(t, serviceA.export(ctx, tmpFile)) closeNodeA() require.NoError(t, dsA.Close()) } _, err = tmpFile.Seek(0, io.SeekStart) require.NoError(t, err) { dsB := dsync.MutexWrap(ds.NewMapDatastore()) secretStoreB, err := secretstore.NewSecretStore(dsB, nil) require.NoError(t, err) ipfsNodeB := ipfsutil.TestingCoreAPIUsingMockNet(ctx, t, &ipfsutil.TestingAPIOpts{ Mocknet: mn, Datastore: dsB, }) odb, err := NewWeshOrbitDB(ctx, ipfsNodeB.API(), &NewOrbitDBOptions{ NewOrbitDBOptions: orbitdb.NewOrbitDBOptions{ PubSub: pubsubraw.NewPubSub(ipfsNodeB.PubSub(), ipfsNodeB.MockNode().PeerHost.ID(), logger, nil), Logger: logger, }, Datastore: dsB, SecretStore: secretStoreB, }) require.NoError(t, err) err = RestoreAccountExport(ctx, tmpFile, ipfsNodeB.API(), odb, logger) require.NoError(t, err) nodeB, closeNodeB := NewTestingProtocol(ctx, t, &TestingOpts{ Mocknet: mn, DiscoveryServer: msrv, SecretStore: secretStoreB, CoreAPIMock: ipfsNodeB, OrbitDB: odb, }, dsB) defer closeNodeB() nodeBInstanceConfig, err := nodeB.Client.ServiceGetConfiguration(ctx, &protocoltypes.ServiceGetConfiguration_Request{}) require.NoError(t, err) require.NotNil(t, nodeBInstanceConfig) require.Equal(t, nodeAInstanceConfig.AccountPk, nodeBInstanceConfig.AccountPk) require.NotEqual(t, nodeAInstanceConfig.DevicePk, nodeBInstanceConfig.DevicePk) require.Equal(t, nodeAInstanceConfig.AccountGroupPk, nodeBInstanceConfig.AccountGroupPk) accountGroup := nodeB.Service.(*service).getAccountGroup() require.NotNil(t, accountGroup) entries := accountGroup.messageStore.OpLog().GetEntries() for _, evt := range entries.Slice() { _, ok := expectedMessages[evt.GetHash()] require.True(t, ok) } _, err = nodeB.Service.ActivateGroup(ctx, &protocoltypes.ActivateGroup_Request{GroupPk: g.PublicKey}) require.NoError(t, err) for _, gPK := range [][]byte{nodeBInstanceConfig.AccountGroupPk, g.PublicKey} { sub, err := nodeB.Client.GroupMessageList( ctx, &protocoltypes.GroupMessageList_Request{ GroupPk: gPK, UntilNow: true, }, ) require.NoError(t, err) for { evt, err := sub.Recv() if err != nil { require.Equal(t, io.EOF, err) break } id, err := cid.Parse(evt.EventContext.Id) require.NoError(t, err) ref, ok := expectedMessages[id] require.True(t, ok) require.Equal(t, ref, evt.Message) delete(expectedMessages, id) } } require.Empty(t, expectedMessages) } // TODO: test account metadata entries } ================================================ FILE: api/go-internal/buf.yaml ================================================ version: v2 name: buf.build/berty-technologies/weshnet breaking: use: - FILE lint: use: - DEFAULT ================================================ FILE: api/go-internal/handshake/handshake.proto ================================================ syntax = "proto3"; package handshake; option go_package = "berty.tech/weshnet/v2/internal/handshake"; message BoxEnvelope { bytes box = 1; } message HelloPayload { bytes ephemeral_pub_key = 1; } message RequesterAuthenticatePayload { bytes requester_account_id = 1; bytes requester_account_sig = 2; } message ResponderAcceptPayload { bytes responder_account_sig = 1; } message RequesterAcknowledgePayload { bool success = 1; } ================================================ FILE: api/go-internal/tinder/records.proto ================================================ syntax = "proto3"; package tinder; option go_package = "berty.tech/weshnet/v2/pkg/tinder"; message Records { repeated Record records = 1; } message Record { string cid = 1; int64 expire = 2; } ================================================ FILE: api/protocol/buf.yaml ================================================ version: v2 name: buf.build/berty-technologies/weshnet deps: - buf.build/srikrsna/protoc-gen-gotag breaking: use: - FILE lint: use: - DEFAULT ================================================ FILE: api/protocol/errcode/errcode.proto ================================================ syntax = "proto3"; package weshnet.errcode; option go_package = "berty.tech/weshnet/v2/pkg/errcode"; enum ErrCode { //---------------- // Special errors //---------------- Undefined = 0; // default value, should never be set manually TODO = 666; // indicates that you plan to create an error later ErrNotImplemented = 777; // indicates that a method is not implemented yet ErrInternal = 888; // indicates an unknown error (without Code), i.e. in gRPC //---------------- // Generic errors //---------------- // Parameters and I/O errors ErrInvalidInput = 100; ErrInvalidRange = 101; ErrMissingInput = 102; ErrSerialization = 103; ErrDeserialization = 104; ErrStreamRead = 105; ErrStreamWrite = 106; ErrStreamTransform = 110; ErrStreamSendAndClose = 111; ErrStreamHeaderWrite = 112; ErrStreamHeaderRead = 115; ErrStreamSink = 113; ErrStreamCloseAndRecv = 114; ErrMissingMapKey = 107; ErrDBWrite = 108; ErrDBRead = 109; ErrDBDestroy = 120; ErrDBMigrate = 121; ErrDBReplay = 122; ErrDBRestore = 123; ErrDBOpen = 124; ErrDBClose = 125; // Crypto errors ErrCryptoRandomGeneration = 200; ErrCryptoKeyGeneration = 201; ErrCryptoNonceGeneration = 202; ErrCryptoSignature = 203; ErrCryptoSignatureVerification = 204; ErrCryptoDecrypt = 205; ErrCryptoDecryptPayload = 206; ErrCryptoEncrypt = 207; ErrCryptoKeyConversion = 208; ErrCryptoCipherInit = 209; ErrCryptoKeyDerivation = 210; // Pattern errors ErrMap = 300; ErrForEach = 301; // Keystore errors ErrKeystoreGet = 400; ErrKeystorePut = 401; ErrNotFound = 404; // generic //----------------- // Specific errors //----------------- // OrbitDB errors ErrOrbitDBInit = 1000; ErrOrbitDBOpen = 1001; ErrOrbitDBAppend = 1002; ErrOrbitDBDeserialization = 1003; ErrOrbitDBStoreCast = 1004; // Handshake errors ErrHandshakeOwnEphemeralKeyGenSend = 1100; ErrHandshakePeerEphemeralKeyRecv = 1101; ErrHandshakeRequesterAuthenticateBoxKeyGen = 1102; ErrHandshakeResponderAcceptBoxKeyGen = 1103; ErrHandshakeRequesterHello = 1104; ErrHandshakeResponderHello = 1105; ErrHandshakeRequesterAuthenticate = 1106; ErrHandshakeResponderAccept = 1107; ErrHandshakeRequesterAcknowledge = 1108; // Contact Request errors ErrContactRequestSameAccount = 1200; ErrContactRequestContactAlreadyAdded = 1201; ErrContactRequestContactBlocked = 1202; ErrContactRequestContactUndefined = 1203; ErrContactRequestIncomingAlreadyReceived = 1204; // Group errors ErrGroupMemberLogEventOpen = 1300; ErrGroupMemberLogEventSignature = 1301; ErrGroupMemberUnknownGroupID = 1302; ErrGroupSecretOtherDestMember = 1303; ErrGroupSecretAlreadySentToMember = 1304; ErrGroupInvalidType = 1305; ErrGroupMissing = 1306; ErrGroupActivate = 1307; ErrGroupDeactivate = 1308; ErrGroupInfo = 1309; ErrGroupUnknown = 1310; ErrGroupOpen = 1311; // Message key errors ErrMessageKeyPersistencePut = 1500; ErrMessageKeyPersistenceGet = 1501; // Services Replication ErrServiceReplication = 4100; ErrServiceReplicationServer = 4101; ErrServiceReplicationMissingEndpoint = 4102; // Services Directory ErrServicesDirectory = 4200; ErrServicesDirectoryInvalidVerifiedCredentialSubject = 4201; ErrServicesDirectoryExistingRecordNotFound = 4202; ErrServicesDirectoryRecordLockedAndCantBeReplaced = 4203; ErrServicesDirectoryExplicitReplaceFlagRequired = 4204; ErrServicesDirectoryInvalidVerifiedCredential = 4205; ErrServicesDirectoryExpiredVerifiedCredential = 4206; ErrServicesDirectoryInvalidVerifiedCredentialID = 4207; } message ErrDetails { repeated ErrCode codes = 1; } ================================================ FILE: api/protocol/outofstoremessagetypes/outofstoremessage.proto ================================================ syntax = "proto3"; package weshnet.outofstoremessage.v1; import "protocoltypes.proto"; option go_package = "berty.tech/weshnet/v2/pkg/outofstoremessagetypes"; // OutOfStoreMessageService is the service used to open out-of-store messages (e.g. push notifications) // It is used to open messages with a lightweight protocol service for mobile backgroup processes. service OutOfStoreMessageService { // OutOfStoreReceive parses a payload received outside a synchronized store rpc OutOfStoreReceive(weshnet.protocol.v1.OutOfStoreReceive.Request) returns (weshnet.protocol.v1.OutOfStoreReceive.Reply); } ================================================ FILE: api/protocol/protocoltypes.proto ================================================ syntax = "proto3"; package weshnet.protocol.v1; option go_package = "berty.tech/weshnet/v2/pkg/protocoltypes"; // ProtocolService is the top-level API to manage the Wesh protocol service. // Each active Wesh protocol service is considered as a Wesh device and is associated with a Wesh user. service ProtocolService { // ServiceExportData exports the current data of the protocol service rpc ServiceExportData (ServiceExportData.Request) returns (stream ServiceExportData.Reply); // ServiceGetConfiguration gets the current configuration of the protocol service rpc ServiceGetConfiguration (ServiceGetConfiguration.Request) returns (ServiceGetConfiguration.Reply); // ContactRequestReference retrieves the information required to create a reference (ie. included in a shareable link) to the current account rpc ContactRequestReference (ContactRequestReference.Request) returns (ContactRequestReference.Reply); // ContactRequestDisable disables incoming contact requests rpc ContactRequestDisable (ContactRequestDisable.Request) returns (ContactRequestDisable.Reply); // ContactRequestEnable enables incoming contact requests rpc ContactRequestEnable (ContactRequestEnable.Request) returns (ContactRequestEnable.Reply); // ContactRequestResetReference changes the contact request reference rpc ContactRequestResetReference (ContactRequestResetReference.Request) returns (ContactRequestResetReference.Reply); // ContactRequestSend attempt to send a contact request rpc ContactRequestSend (ContactRequestSend.Request) returns (ContactRequestSend.Reply); // ContactRequestAccept accepts a contact request rpc ContactRequestAccept (ContactRequestAccept.Request) returns (ContactRequestAccept.Reply); // ContactRequestDiscard ignores a contact request, without informing the other user rpc ContactRequestDiscard (ContactRequestDiscard.Request) returns (ContactRequestDiscard.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. rpc ShareContact (ShareContact.Request) returns (ShareContact.Reply); // DecodeContact decodes the Protobuf encoding of a shareable contact which was returned by ShareContact. rpc DecodeContact (DecodeContact.Request) returns (DecodeContact.Reply); // ContactBlock blocks a contact from sending requests rpc ContactBlock (ContactBlock.Request) returns (ContactBlock.Reply); // ContactUnblock unblocks a contact from sending requests rpc ContactUnblock (ContactUnblock.Request) returns (ContactUnblock.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 rpc ContactAliasKeySend (ContactAliasKeySend.Request) returns (ContactAliasKeySend.Reply); // MultiMemberGroupCreate creates a new multi-member group rpc MultiMemberGroupCreate (MultiMemberGroupCreate.Request) returns (MultiMemberGroupCreate.Reply); // MultiMemberGroupJoin joins a multi-member group rpc MultiMemberGroupJoin (MultiMemberGroupJoin.Request) returns (MultiMemberGroupJoin.Reply); // MultiMemberGroupLeave leaves a multi-member group rpc MultiMemberGroupLeave (MultiMemberGroupLeave.Request) returns (MultiMemberGroupLeave.Reply); // MultiMemberGroupAliasResolverDisclose discloses your alias resolver key rpc MultiMemberGroupAliasResolverDisclose (MultiMemberGroupAliasResolverDisclose.Request) returns (MultiMemberGroupAliasResolverDisclose.Reply); // MultiMemberGroupAdminRoleGrant grants an admin role to a group member rpc MultiMemberGroupAdminRoleGrant (MultiMemberGroupAdminRoleGrant.Request) returns (MultiMemberGroupAdminRoleGrant.Reply); // MultiMemberGroupInvitationCreate creates an invitation to a multi-member group rpc MultiMemberGroupInvitationCreate (MultiMemberGroupInvitationCreate.Request) returns (MultiMemberGroupInvitationCreate.Reply); // AppMetadataSend adds an app event to the metadata store, the message is encrypted using a symmetric key and readable by future group members rpc AppMetadataSend (AppMetadataSend.Request) returns (AppMetadataSend.Reply); // AppMessageSend adds an app event to the message store, the message is encrypted using a derived key and readable by current group members rpc AppMessageSend (AppMessageSend.Request) returns (AppMessageSend.Reply); // GroupMetadataList replays previous and subscribes to new metadata events from the group rpc GroupMetadataList (GroupMetadataList.Request) returns (stream GroupMetadataEvent); // GroupMessageList replays previous and subscribes to new message events from the group rpc GroupMessageList (GroupMessageList.Request) returns (stream GroupMessageEvent); // GroupInfo retrieves information about a group rpc GroupInfo (GroupInfo.Request) returns (GroupInfo.Reply); // ActivateGroup explicitly opens a group rpc ActivateGroup (ActivateGroup.Request) returns (ActivateGroup.Reply); // DeactivateGroup closes a group rpc DeactivateGroup (DeactivateGroup.Request) returns (DeactivateGroup.Reply); // GroupDeviceStatus monitor device status rpc GroupDeviceStatus(GroupDeviceStatus.Request) returns (stream GroupDeviceStatus.Reply); rpc DebugListGroups (DebugListGroups.Request) returns (stream DebugListGroups.Reply); rpc DebugInspectGroupStore (DebugInspectGroupStore.Request) returns (stream DebugInspectGroupStore.Reply); rpc DebugGroup (DebugGroup.Request) returns (DebugGroup.Reply); rpc SystemInfo (SystemInfo.Request) returns (SystemInfo.Reply); // CredentialVerificationServiceInitFlow Initialize a credential verification flow rpc CredentialVerificationServiceInitFlow (CredentialVerificationServiceInitFlow.Request) returns (CredentialVerificationServiceInitFlow.Reply); // CredentialVerificationServiceCompleteFlow Completes a credential verification flow rpc CredentialVerificationServiceCompleteFlow (CredentialVerificationServiceCompleteFlow.Request) returns (CredentialVerificationServiceCompleteFlow.Reply); // VerifiedCredentialsList Retrieves the list of verified credentials rpc VerifiedCredentialsList (VerifiedCredentialsList.Request) returns (stream VerifiedCredentialsList.Reply); // ReplicationServiceRegisterGroup Asks a replication service to distribute a group contents rpc ReplicationServiceRegisterGroup (ReplicationServiceRegisterGroup.Request) returns (ReplicationServiceRegisterGroup.Reply); // PeerList returns a list of P2P peers rpc PeerList(PeerList.Request) returns (PeerList.Reply); // OutOfStoreReceive parses a payload received outside a synchronized store rpc OutOfStoreReceive(OutOfStoreReceive.Request) returns (OutOfStoreReceive.Reply); // OutOfStoreSeal creates a payload of a message present in store to be sent outside a synchronized store rpc OutOfStoreSeal(OutOfStoreSeal.Request) returns (OutOfStoreSeal.Reply); // RefreshContactRequest try to refresh the contact request for the given contact rpc RefreshContactRequest(RefreshContactRequest.Request) returns (RefreshContactRequest.Reply); } enum GroupType { // GroupTypeUndefined indicates that the value has not been set. For example, happens if group is replicated. GroupTypeUndefined = 0; // GroupTypeAccount is the group managing an account, available to all its devices. GroupTypeAccount = 1; // GroupTypeContact is the group created between two accounts, available to all their devices. GroupTypeContact = 2; // GroupTypeMultiMember is a group containing an undefined number of members. GroupTypeMultiMember = 3; // Following group types have not been defined, first is a group with // only approved writers, second is public group with anyone allowed to // write, in both cases full history is available to new members. // // GroupTypeChannel = 4; // GroupTypePublic = 5; } enum EventType { // EventTypeUndefined indicates that the value has not been set. Should not happen. EventTypeUndefined = 0; // EventTypeGroupMemberDeviceAdded indicates the payload includes that a member has added their device to the group EventTypeGroupMemberDeviceAdded = 1; // EventTypeGroupDeviceChainKeyAdded indicates the payload includes that a member has sent their device chain key to another member EventTypeGroupDeviceChainKeyAdded = 2; // EventTypeGroupAdditionalRendezvousSeedAdded adds a new rendezvous seed to a group // Might be implemented later, could be useful for replication services // EventTypeGroupAdditionalRendezvousSeedAdded = 3; // EventTypeGroupAdditionalRendezvousSeedRemoved removes a rendezvous seed from a group // Might be implemented later, could be useful for replication services // EventTypeGroupAdditionalRendezvousSeedRemoved = 4; // EventTypeAccountGroupJoined indicates the payload includes that the account has joined a group EventTypeAccountGroupJoined = 101; // EventTypeAccountGroupLeft indicates the payload includes that the account has left a group EventTypeAccountGroupLeft = 102; // EventTypeAccountContactRequestDisabled indicates the payload includes that the account has disabled incoming contact requests EventTypeAccountContactRequestDisabled = 103; // EventTypeAccountContactRequestEnabled indicates the payload includes that the account has enabled incoming contact requests EventTypeAccountContactRequestEnabled = 104; // EventTypeAccountContactRequestReferenceReset indicates the payload includes that the account has a new contact request rendezvous seed EventTypeAccountContactRequestReferenceReset = 105; // EventTypeAccountContactRequestOutgoingEnqueued indicates the payload includes that the account will attempt to send a new contact request EventTypeAccountContactRequestOutgoingEnqueued = 106; // EventTypeAccountContactRequestOutgoingSent indicates the payload includes that the account has sent a contact request EventTypeAccountContactRequestOutgoingSent = 107; // EventTypeAccountContactRequestIncomingReceived indicates the payload includes that the account has received a contact request EventTypeAccountContactRequestIncomingReceived = 108; // EventTypeAccountContactRequestIncomingDiscarded indicates the payload includes that the account has ignored a contact request EventTypeAccountContactRequestIncomingDiscarded = 109; // EventTypeAccountContactRequestIncomingAccepted indicates the payload includes that the account has accepted a contact request EventTypeAccountContactRequestIncomingAccepted = 110; // EventTypeAccountContactBlocked indicates the payload includes that the account has blocked a contact EventTypeAccountContactBlocked = 111; // EventTypeAccountContactUnblocked indicates the payload includes that the account has unblocked a contact EventTypeAccountContactUnblocked = 112; // EventTypeContactAliasKeyAdded indicates the payload includes that the contact group has received an alias key EventTypeContactAliasKeyAdded = 201; // EventTypeMultiMemberGroupAliasResolverAdded indicates the payload includes that a member of the group sent their alias proof EventTypeMultiMemberGroupAliasResolverAdded = 301; // EventTypeMultiMemberGroupInitialMemberAnnounced indicates the payload includes that a member has authenticated themselves as the group owner EventTypeMultiMemberGroupInitialMemberAnnounced = 302; // EventTypeMultiMemberGroupAdminRoleGranted indicates the payload includes that an admin of the group granted another member as an admin EventTypeMultiMemberGroupAdminRoleGranted = 303; // EventTypeGroupReplicating indicates that the group has been registered for replication on a server EventTypeGroupReplicating = 403; // EventTypeAccountVerifiedCredentialRegistered EventTypeAccountVerifiedCredentialRegistered = 500; // EventTypeGroupMetadataPayloadSent indicates the payload includes an app specific event, unlike messages stored on the message store it is encrypted using a static key EventTypeGroupMetadataPayloadSent = 1001; } // Account describes all the secrets that identifies an Account message Account { // group specifies which group is used to manage the account Group group = 1; // 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 bytes account_private_key = 2; // 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 bytes alias_private_key = 3; // public_rendezvous_seed, rendezvous seed used for direct communication bytes public_rendezvous_seed = 4; } // Group define a group and is enough to invite someone to it message Group { // public_key is the identifier of the group, it signs the group secret and the initial member of a multi-member group bytes public_key = 1; // secret is the symmetric secret of the group, which is used to encrypt the metadata bytes secret = 2; // secret_sig is the signature of the secret used to ensure the validity of the group bytes secret_sig = 3; // group_type specifies the type of the group, used to determine how device chain key is generated GroupType group_type = 4; // sign_pub is the signature public key used to verify entries, not required when secret and secret_sig are provided bytes sign_pub = 5; // link_key is the secret key used to exchange group updates and links to attachments, useful for replication services bytes link_key = 6; // link_key_sig is the signature of the link_key using the group private key bytes link_key_sig = 7; } message GroupHeadsExport { // public_key is the identifier of the group, it signs the group secret and the initial member of a multi-member group bytes public_key = 1; // sign_pub is the signature public key used to verify entries bytes sign_pub = 2; // metadata_heads_cids are the heads of the metadata store that should be restored from an export repeated bytes metadata_heads_cids = 3; // messages_heads_cids are the heads of the metadata store that should be restored from an export repeated bytes messages_heads_cids = 4; // link_key bytes link_key = 5; } // GroupMetadata is used in GroupEnvelope and only readable by invited group members message GroupMetadata { // event_type defines which event type is used EventType event_type = 1; // the serialization depends on event_type, event is symmetrically encrypted bytes payload = 2; // sig is the signature of the payload, it depends on the event_type for the used key bytes sig = 3; // protocol_metadata is protocol layer data ProtocolMetadata protocol_metadata = 4; } // GroupEnvelope is a publicly exposed structure containing a group metadata event message GroupEnvelope { // nonce is used to encrypt the message bytes nonce = 1; // event is encrypted using a symmetric key shared among group members bytes event = 2; reserved 3; // repeated bytes encrypted_attachment_cids = 3 ; } // MessageHeaders is used in MessageEnvelope and only readable by invited group members message MessageHeaders { // counter is the current counter value for the specified device uint64 counter = 1; // device_pk is the public key of the device sending the message bytes device_pk = 2; // sig is the signature of the encrypted message using the device's private key bytes sig = 3; // metadata allow to pass custom informations map metadata = 4; } message ProtocolMetadata { // attachments_secrets is a list of secret keys used retrieve attachments reserved 1; //repeated bytes attachments_secrets = 1; } // EncryptedMessage is used in MessageEnvelope and only readable by groups members that joined before the message was sent message EncryptedMessage { // plaintext is the app layer data bytes plaintext = 1; // protocol_metadata is protocol layer data ProtocolMetadata protocol_metadata = 2; } // MessageEnvelope is a publicly exposed structure containing a group secure message message MessageEnvelope { // message_headers is an encrypted serialization using a symmetric key of a MessageHeaders message bytes message_headers = 1; // message is an encrypted message, only readable by group members who previously received the appropriate chain key bytes message = 2; // nonce is a nonce for message headers bytes nonce = 3; // encrypted_attachment_cids is a list of attachment CIDs encrypted specifically for replication services reserved 4; // repeated bytes encrypted_attachment_cids = 4; } // *************************************************************************** // Group event types // *************************************************************************** // EventContext adds context (its id, its parents and its attachments) to an event message EventContext { // id is the CID of the underlying OrbitDB event bytes id = 1; // id are the the CIDs of the underlying parents of the OrbitDB event repeated bytes parent_ids = 2; // group_pk receiving the event bytes group_pk = 3; // attachment_cids is a list of attachment that can be retrieved reserved 4; // repeated bytes attachment_cids = 4; } // GroupMetadataPayloadSent is an app defined message, accessible to future group members message GroupMetadataPayloadSent { // device_pk is the device sending the event, signs the message bytes device_pk = 1; // message is the payload bytes message = 2; } // ContactAliasKeyAdded is an event type where ones shares their alias public key message ContactAliasKeyAdded { // device_pk is the device sending the event, signs the message bytes device_pk = 1; // alias_pk is the alias key which will be used to verify a contact identity bytes alias_pk = 2; } // GroupMemberDeviceAdded is an event which indicates to a group a new device (and eventually a new member) is joining it // When added on AccountGroup, this event should be followed by appropriate GroupMemberDeviceAdded and GroupDeviceChainKeyAdded events message GroupMemberDeviceAdded { // member_pk is the member sending the event bytes member_pk = 1; // device_pk is the device sending the event, signs the message bytes device_pk = 2; // member_sig is used to prove the ownership of the member pk bytes member_sig = 3; // TODO: signature of what ??? ensure it can't be replayed } // DeviceChainKey is a chain key, which will be encrypted for a specific member of the group message DeviceChainKey { // chain_key is the current value of the chain key of the group device bytes chain_key = 1; // counter is the current value of the counter of the group device uint64 counter = 2; } // GroupDeviceChainKeyAdded is an event which indicates to a group member a device chain key message GroupDeviceChainKeyAdded { // device_pk is the device sending the event, signs the message bytes device_pk = 1; // dest_member_pk is the member who should receive the secret bytes dest_member_pk = 2; // payload is the serialization of Payload encrypted for the specified member bytes payload = 3; } // MultiMemberGroupAliasResolverAdded indicates that a group member want to disclose their presence in the group to their contacts message MultiMemberGroupAliasResolverAdded { // device_pk is the device sending the event, signs the message bytes device_pk = 1; // 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) bytes alias_resolver = 2; // alias_proof ensures that the associated alias_resolver has been issued by the right account // Generated using aliasSKSig(GroupID) bytes alias_proof = 3; } // MultiMemberGroupAdminRoleGranted indicates that a group admin allows another group member to act as an admin message MultiMemberGroupAdminRoleGranted { // device_pk is the device sending the event, signs the message, must be the device of an admin of the group bytes device_pk = 1; // grantee_member_pk is the member public key of the member granted of the admin role bytes grantee_member_pk = 2; } // MultiMemberGroupInitialMemberAnnounced indicates that a member is the group creator, this event is signed using the group ID private key message MultiMemberGroupInitialMemberAnnounced { // member_pk is the public key of the member who is the group creator bytes member_pk = 1; } // GroupAddAdditionalRendezvousSeed indicates that an additional rendezvous point should be used for data synchronization message GroupAddAdditionalRendezvousSeed { // device_pk is the device sending the event, signs the message, must be the device of an admin of the group bytes device_pk = 1; // seed is the additional rendezvous point seed which should be used bytes seed = 2; } // GroupRemoveAdditionalRendezvousSeed indicates that a previously added rendezvous point should be removed message GroupRemoveAdditionalRendezvousSeed { // device_pk is the device sending the event, signs the message, must be the device of an admin of the group bytes device_pk = 1; // seed is the additional rendezvous point seed which should be removed bytes seed = 2; } // AccountGroupJoined indicates that the account is now part of a new group message AccountGroupJoined { // device_pk is the device sending the event, signs the message bytes device_pk = 1; // group describe the joined group Group group = 2; } // AccountGroupLeft indicates that the account has left a group message AccountGroupLeft { // device_pk is the device sending the event, signs the message bytes device_pk = 1; // group_pk references the group left bytes group_pk = 2; } // AccountContactRequestDisabled indicates that the account should not be advertised on a public rendezvous point message AccountContactRequestDisabled { // device_pk is the device sending the event, signs the message bytes device_pk = 1; } // AccountContactRequestEnabled indicates that the account should be advertised on a public rendezvous point message AccountContactRequestEnabled { // device_pk is the device sending the event, signs the message bytes device_pk = 1; } // AccountContactRequestReferenceReset indicates that the account should be advertised on different public rendezvous points message AccountContactRequestReferenceReset { // device_pk is the device sending the event, signs the message bytes device_pk = 1; // public_rendezvous_seed is the new rendezvous point seed bytes public_rendezvous_seed = 2; } // This event should be followed by an AccountGroupJoined event // This event should be followed by a GroupMemberDeviceAdded event within the AccountGroup // This event should be followed by a GroupDeviceChainKeyAdded event within the AccountGroup // AccountContactRequestOutgoingEnqueued indicates that the account will attempt to send a contact request when a matching peer is discovered message AccountContactRequestOutgoingEnqueued { // device_pk is the device sending the event, signs the message bytes device_pk = 1; // group_pk is the 1to1 group with the requested user bytes group_pk = 2; // contact is a message describing how to connect to the other account ShareableContact contact = 3; // own_metadata is the identifying metadata that will be shared to the other account bytes own_metadata = 4; } // AccountContactRequestOutgoingSent indicates that the account has sent a contact request message AccountContactRequestOutgoingSent { // device_pk is the device sending the account event, signs the message bytes device_pk = 1; // contact_pk is the contacted account bytes contact_pk = 2; } // AccountContactRequestIncomingReceived indicates that the account has received a new contact request message AccountContactRequestIncomingReceived { // device_pk is the device sending the account event (which received the contact request), signs the message bytes device_pk = 1; // contact_pk is the account sending the request bytes contact_pk = 2; // TODO: is this necessary? // contact_rendezvous_seed is the rendezvous seed of the contact sending the request bytes contact_rendezvous_seed = 3; // TODO: is this necessary? // contact_metadata is the metadata specific to the app to identify the contact for the request bytes contact_metadata = 4; } // AccountContactRequestIncomingDiscarded indicates that a contact request has been refused message AccountContactRequestIncomingDiscarded { // device_pk is the device sending the event, signs the message bytes device_pk = 1; // contact_pk is the contact whom request is refused bytes contact_pk = 2; } // This event should be followed by an AccountGroupJoined event // This event should be followed by GroupMemberDeviceAdded and GroupDeviceChainKeyAdded events within the AccountGroup // AccountContactRequestIncomingAccepted indicates that a contact request has been accepted message AccountContactRequestIncomingAccepted { // device_pk is the device sending the event, signs the message bytes device_pk = 1; // contact_pk is the contact whom request is accepted bytes contact_pk = 2; // group_pk is the 1to1 group with the requester user bytes group_pk = 3; } // AccountContactBlocked indicates that a contact is blocked message AccountContactBlocked { // device_pk is the device sending the event, signs the message bytes device_pk = 1; // contact_pk is the contact blocked bytes contact_pk = 2; } // AccountContactUnblocked indicates that a contact is unblocked message AccountContactUnblocked { // device_pk is the device sending the event, signs the message bytes device_pk = 1; // contact_pk is the contact unblocked bytes contact_pk = 2; } message GroupReplicating { // device_pk is the device sending the event, signs the message bytes device_pk = 1; // authentication_url indicates which server has been used for authentication string authentication_url = 2; // replication_server indicates which server will be used for replication string replication_server = 3; } // *************************************************************************** // RPC methods inputs and outputs // *************************************************************************** message ServiceExportData { message Request {} message Reply { bytes exported_data = 1; } } message ServiceGetConfiguration { enum SettingState { Unknown = 0; Enabled = 1; Disabled = 2; Unavailable = 3; } message Request {} message Reply { // account_pk is the public key of the current account bytes account_pk = 1; // device_pk is the public key of the current device bytes device_pk = 2; // account_group_pk is the public key of the account group bytes account_group_pk = 3; // peer_id is the peer ID of the current IPFS node string peer_id = 4; // listeners is the list of swarm listening addresses of the current IPFS node repeated string listeners = 5; SettingState ble_enabled = 6; SettingState wifi_p2p_enabled = 7; // MultiPeerConnectivity for Darwin and Nearby for Android SettingState mdns_enabled = 8; SettingState relay_enabled = 9; } } message ContactRequestReference { message Request {} message Reply { // public_rendezvous_seed is the rendezvous seed used by the current account bytes public_rendezvous_seed = 1; // enabled indicates if incoming contact requests are enabled bool enabled = 2; } } message ContactRequestDisable { message Request {} message Reply {} } message ContactRequestEnable { message Request {} message Reply { // public_rendezvous_seed is the rendezvous seed used by the current account bytes public_rendezvous_seed = 1; } } message ContactRequestResetReference { message Request {} message Reply { // public_rendezvous_seed is the rendezvous seed used by the current account bytes public_rendezvous_seed = 1; } } message ContactRequestSend { message Request { // contact is a message describing how to connect to the other account ShareableContact contact = 1; // own_metadata is the identifying metadata that will be shared to the other account bytes own_metadata = 2; } message Reply {} } message ContactRequestAccept { message Request { // contact_pk is the identifier of the contact to accept the request from bytes contact_pk = 1; } message Reply {} } message ContactRequestDiscard { message Request { // contact_pk is the identifier of the contact to ignore the request from bytes contact_pk = 1; } message Reply {} } message ShareContact { message Request {} message Reply { // encoded_contact is the Protobuf encoding of the ShareableContact. You can further encode the bytes for sharing, such as base58 or QR code. bytes encoded_contact = 1; } } message DecodeContact { message Request { // encoded_contact is the Protobuf encoding of the shareable contact (as returned by ShareContact). bytes encoded_contact = 1; } message Reply { // shareable_contact is the decoded shareable contact. ShareableContact contact = 1; } } message ContactBlock { message Request { // contact_pk is the identifier of the contact to block bytes contact_pk = 1; } message Reply {} } message ContactUnblock { message Request { // contact_pk is the identifier of the contact to unblock bytes contact_pk = 1; } message Reply {} } message ContactAliasKeySend { message Request { // contact_pk is the identifier of the contact to send the alias public key to bytes group_pk = 1; } message Reply {} } message MultiMemberGroupCreate { message Request {} message Reply { // group_pk is the identifier of the newly created group bytes group_pk = 1; } } message MultiMemberGroupJoin { message Request { // group is the information of the group to join Group group = 1; } message Reply {} } message MultiMemberGroupLeave { message Request { bytes group_pk = 1; } message Reply {} } message MultiMemberGroupAliasResolverDisclose { message Request { // group_pk is the identifier of the group bytes group_pk = 1; } message Reply {} } message MultiMemberGroupAdminRoleGrant { message Request { // group_pk is the identifier of the group bytes group_pk = 1; // member_pk is the identifier of the member which will be granted the admin role bytes member_pk = 2; } message Reply {} } message MultiMemberGroupInvitationCreate { message Request { // group_pk is the identifier of the group bytes group_pk = 1; } message Reply { // group is the invitation to the group Group group = 1; } } message AppMetadataSend { message Request { // group_pk is the identifier of the group bytes group_pk = 1; // payload is the payload to send bytes payload = 2; // attachment_cids is a list of attachment cids reserved 3; // repeated bytes attachment_cids = 3; } message Reply { bytes cid = 1; } } message AppMessageSend { message Request { // group_pk is the identifier of the group bytes group_pk = 1; // payload is the payload to send bytes payload = 2; // attachment_cids is a list of attachment cids reserved 3; // repeated bytes attachment_cids = 3; } message Reply { bytes cid = 1; } } message GroupMetadataEvent { // event_context contains context information about the event EventContext event_context = 1; // metadata contains the newly available metadata GroupMetadata metadata = 2; // event_clear clear bytes for the event bytes event = 3; } message GroupMessageEvent { // event_context contains context information about the event EventContext event_context = 1; // headers contains headers of the secure message MessageHeaders headers = 2; // message contains the secure message payload bytes message = 3; } message GroupMetadataList { message Request { // group_pk is the identifier of the group bytes group_pk = 1; // since is the lower ID bound used to filter events // if not set, will return events since the beginning bytes since_id = 2; // since_now will list only new event to come // since_id must not be set bool since_now = 3; // until is the upper ID bound used to filter events // if not set, will subscribe to new events to come bytes until_id = 4; // until_now will not list new event to come // until_id must not be set bool until_now = 5; // reverse_order indicates whether the previous events should be returned in // reverse chronological order bool reverse_order = 6; } } message GroupMessageList { message Request { // group_pk is the identifier of the group bytes group_pk = 1; // since is the lower ID bound used to filter events // if not set, will return events since the beginning bytes since_id = 2; // since_now will list only new event to come // since_id must not be set bool since_now = 3; // until is the upper ID bound used to filter events // if not set, will subscribe to new events to come bytes until_id = 4; // until_now will not list new event to come // until_id must not be set bool until_now = 5; // reverse_order indicates whether the previous events should be returned in // reverse chronological order bool reverse_order = 6; } } message GroupInfo { message Request { // group_pk is the identifier of the group bytes group_pk = 1; // contact_pk is the identifier of the contact bytes contact_pk = 2; } message Reply { // group is the group invitation, containing the group pk and its type Group group = 1; // member_pk is the identifier of the current member in the group bytes member_pk = 2; // device_pk is the identifier of the current device in the group bytes device_pk = 3; } } message ActivateGroup { message Request { // group_pk is the identifier of the group bytes group_pk = 1; // local_only will open the group without enabling network interactions // with other members bool local_only = 2; } message Reply { } } message DeactivateGroup { message Request { // group_pk is the identifier of the group bytes group_pk = 1; } message Reply { } } message GroupDeviceStatus { enum Type { TypeUnknown = 0; TypePeerDisconnected = 1; TypePeerConnected = 2; TypePeerReconnecting = 3; } enum Transport { TptUnknown = 0; TptLAN = 1; TptWAN = 2; TptProximity = 3; } message Request { bytes group_pk = 1; } message Reply { message PeerConnected { string peer_id = 1; bytes device_pk = 2; repeated Transport transports = 3; repeated string maddrs = 4; } message PeerReconnecting { string peer_id = 1; } message PeerDisconnected { string peer_id = 1; } Type type = 1; bytes event = 2; } } message DebugListGroups { message Request { } message Reply { // group_pk is the public key of the group bytes group_pk = 1; // group_type is the type of the group GroupType group_type = 2; // contact_pk is the contact public key if appropriate bytes contact_pk = 3; } } message DebugInspectGroupStore { message Request { // group_pk is the identifier of the group bytes group_pk = 1; // log_type is the log to inspect DebugInspectGroupLogType log_type = 2; } message Reply { // cid is the CID of the IPFS log entry bytes cid = 1; // parent_cids is the list of the parent entries repeated bytes parent_cids = 2 ; // event_type metadata event type if subscribed to metadata events EventType metadata_event_type = 3; // device_pk is the public key of the device signing the entry bytes device_pk = 4; // payload is the un encrypted entry payload if available bytes payload = 6; } } message DebugGroup { message Request { // group_pk is the identifier of the group bytes group_pk = 1; } message Reply { // peer_ids is the list of peer ids connected to the same group repeated string peer_ids = 1 ; } } enum DebugInspectGroupLogType { DebugInspectGroupLogTypeUndefined = 0; DebugInspectGroupLogTypeMessage = 1; DebugInspectGroupLogTypeMetadata = 2; } enum ContactState { ContactStateUndefined = 0; ContactStateToRequest = 1; ContactStateReceived = 2; ContactStateAdded = 3; ContactStateRemoved = 4; ContactStateDiscarded = 5; ContactStateBlocked = 6; } message ShareableContact { // pk is the account to send a contact request to bytes pk = 1; // public_rendezvous_seed is the rendezvous seed used by the account to send a contact request to bytes public_rendezvous_seed = 2; // metadata is the metadata specific to the app to identify the contact for the request bytes metadata = 3; } message ServiceTokenSupportedService { string service_type = 1; string service_endpoint = 2; } message ServiceToken { string token = 1; string authentication_url = 2 ; repeated ServiceTokenSupportedService supported_services = 3; int64 expiration = 4; } message CredentialVerificationServiceInitFlow { message Request { string service_url = 1; bytes public_key = 2; string link = 3; } message Reply { string url = 1; bool secure_url = 2; } } message CredentialVerificationServiceCompleteFlow { message Request { string callback_uri = 1; } message Reply { string identifier = 1; } } message VerifiedCredentialsList { message Request { string filter_identifier = 1; string filter_issuer = 2; bool exclude_expired = 3; } message Reply { AccountVerifiedCredentialRegistered credential = 1; } } message ReplicationServiceRegisterGroup { message Request{ bytes group_pk = 1; string token = 2; string authentication_url = 3; string replication_server = 4; } message Reply{} } message ReplicationServiceReplicateGroup { message Request { Group group = 1; } message Reply { bool ok = 1; } } message SystemInfo { message Request {} message Reply { Process process = 1; P2P p2p = 2; OrbitDB orbitdb = 3; repeated string warns = 4; } message OrbitDB { ReplicationStatus account_metadata = 1; message ReplicationStatus { int64 progress = 1; int64 maximum = 2; int64 buffered = 3; int64 queued = 4; } } message P2P { int64 connected_peers = 1; } message Process { string version = 1; string vcs_ref = 2; int64 uptime_ms = 3; int64 user_cpu_time_ms = 10; int64 system_cpu_time_ms = 11; int64 started_at = 12; uint64 rlimit_cur = 13; int64 num_goroutine = 14; int64 nofile = 15; bool too_many_open_files = 16; int64 num_cpu = 17; string go_version = 18; string operating_system = 19; string host_name = 20; string arch = 21; uint64 rlimit_max = 22; int64 pid = 23; int64 ppid = 24; int64 priority = 25; int64 uid = 26; string working_dir = 27; string system_username = 28; } } message PeerList { message Request {} message Reply { repeated Peer peers = 1; } message Peer { // id is the libp2p.PeerID. string id = 1; // routes are the list of active and known maddr. repeated Route routes = 2; // errors is a list of errors related to the peer. repeated string errors = 3; // Features is a list of available features. repeated Feature features = 4; // MinLatency is the minimum latency across all the peer routes. int64 min_latency = 5; // IsActive is true if at least one of the route is active. bool is_active = 6; // Direction is the aggregate of all the routes's direction. Direction direction = 7; } message Route { // IsActive indicates whether the address is currently used or just known. bool is_active = 1; // Address is the multiaddress via which we are connected with the peer. string address = 2; // Direction is which way the connection was established. Direction direction = 3; // Latency is the last known round trip time to the peer in ms. int64 latency = 4; // Streams returns list of streams established with the peer. repeated Stream streams = 5; } message Stream { // id is an identifier used to write protocol headers in streams. string id = 1; } enum Feature { UnknownFeature = 0; WeshFeature = 1; BLEFeature = 2; LocalFeature = 3; TorFeature = 4; QuicFeature = 5; } } enum Direction { UnknownDir = 0; InboundDir = 1; OutboundDir = 2; BiDir = 3; } // Progress define a generic object that can be used to display a progress bar for long-running actions. message Progress { string state = 1; string doing = 2; float progress = 3; uint64 completed = 4; uint64 total = 5; uint64 delay = 6; } message OutOfStoreMessage { bytes cid = 1; bytes device_pk = 2; fixed64 counter = 3; bytes sig = 4; fixed32 flags = 5; bytes encrypted_payload = 6; bytes nonce = 7; } message OutOfStoreMessageEnvelope { bytes nonce = 1; bytes box = 2; bytes group_reference = 3; } message OutOfStoreReceive { message Request { bytes payload = 1; } message Reply { OutOfStoreMessage message = 1; bytes cleartext = 2; bytes group_public_key = 3; bool already_received = 4; } } message OutOfStoreSeal { message Request { bytes cid = 1; bytes group_public_key = 2; } message Reply { bytes encrypted = 1; } } message AccountVerifiedCredentialRegistered { // device_pk is the public key of the device sending the message bytes device_pk = 1; bytes signed_identity_public_key = 2; string verified_credential = 3; int64 registration_date = 4; int64 expiration_date = 5; string identifier = 6; string issuer = 7; } message FirstLastCounters { uint64 first = 1; uint64 last = 2; } // OrbitDBMessageHeads is the payload sent on orbitdb to share peer's heads message OrbitDBMessageHeads { message Box { string address = 1; bytes heads = 2; bytes device_pk = 3; bytes peer_id = 4; } // sealed box should contain encrypted Box bytes sealed_box = 2; // current topic used bytes raw_rotation = 3; } message RefreshContactRequest { message Peer { // id is the libp2p.PeerID. string id = 1; // list of peers multiaddrs. repeated string addrs = 2; } message Request { bytes contact_pk = 1; // timeout in second int64 timeout = 2; } message Reply { // peers found and successfully connected. repeated Peer peers_found = 1; } } ================================================ FILE: api/protocol/replicationtypes/bertyreplication.proto ================================================ syntax = "proto3"; package weshnet.replication.v1; import "protocoltypes.proto"; import "tagger/tagger.proto"; option go_package = "berty.tech/weshnet/v2/pkg/replicationtypes"; // ReplicationService service ReplicationService { // ReplicateGroup rpc ReplicateGroup(ReplicationServiceReplicateGroup.Request) returns (ReplicationServiceReplicateGroup.Reply); rpc ReplicateGlobalStats(ReplicateGlobalStats.Request) returns (ReplicateGlobalStats.Reply); rpc ReplicateGroupStats(ReplicateGroupStats.Request) returns (ReplicateGroupStats.Reply); } message ReplicatedGroup { string public_key = 1 [(tagger.tags) = "gorm:\"primaryKey\""]; string sign_pub = 2; string link_key = 3; int64 created_at = 100; int64 updated_at = 101; int64 metadata_entries_count = 102; string metadata_latest_head = 103; int64 message_entries_count = 104; string message_latest_head = 105; } message ReplicatedGroupToken { string replicated_group_public_key = 1 [(tagger.tags) = "gorm:\"index;primaryKey;autoIncrement:false\""]; ReplicatedGroup replicated_group = 2; string token_issuer = 3 [(tagger.tags) = "gorm:\"primaryKey;autoIncrement:false\""]; string token_id = 4 [(tagger.tags) = "gorm:\"primaryKey;autoIncrement:false\""]; int64 created_at = 5; } message ReplicationServiceReplicateGroup { message Request { weshnet.protocol.v1.Group group = 1; } message Reply { bool ok = 1; } } message ReplicateGlobalStats { message Request {} message Reply { int64 started_at = 1; int64 replicated_groups = 2; int64 total_metadata_entries = 3; int64 total_message_entries = 4; } } message ReplicateGroupStats { message Request { string group_public_key = 1; } message Reply { ReplicatedGroup group = 1; } } ================================================ FILE: api/protocol/verifiablecredstypes/bertyverifiablecreds.proto ================================================ syntax = "proto3"; package weshnet.account.v1; option go_package = "berty.tech/weshnet/v2/pkg/verifiablecredstypes"; // StateChallenge serialized and signed state used when requesting a challenge message StateChallenge { bytes timestamp = 1; bytes nonce = 2; string berty_link = 3; string redirect_uri = 4; string state = 5; } // StateCode serialized and signed state used when requesting a code message StateCode { bytes timestamp = 1; string berty_link = 2; CodeStrategy code_strategy = 3; string identifier = 4; string code = 5; string redirect_uri = 6; string state = 7; } message AccountCryptoChallenge { string challenge = 1; } enum FlowType { FlowTypeUndefined = 0; // FlowTypeCode asks users a code sent on a side channel FlowTypeCode = 1; // FlowTypeAuth currently unimplemented FlowTypeAuth = 2; // FlowTypeProof currently unimplemented FlowTypeProof = 3; } enum CodeStrategy { CodeStrategyUndefined = 0; // CodeStrategy6Digits currently unimplemented CodeStrategy6Digits = 1; // CodeStrategy10Chars currently unimplemented CodeStrategy10Chars = 2; // CodeStrategyMocked6Zeroes must only be used in testing CodeStrategyMocked6Zeroes = 999; } ================================================ FILE: api_app.go ================================================ package weshnet import ( "context" "encoding/base64" "fmt" "github.com/ipfs/go-cid" "go.uber.org/zap" "google.golang.org/protobuf/proto" "berty.tech/weshnet/v2/pkg/errcode" "berty.tech/weshnet/v2/pkg/protocoltypes" "berty.tech/weshnet/v2/pkg/tyber" ) func (s *service) AppMetadataSend(ctx context.Context, req *protocoltypes.AppMetadataSend_Request) (_ *protocoltypes.AppMetadataSend_Reply, err error) { ctx, _, endSection := tyber.Section(ctx, s.logger, fmt.Sprintf("Sending app metadata to group %s", base64.RawURLEncoding.EncodeToString(req.GroupPk))) defer func() { endSection(err, "") }() gc, err := s.GetContextGroupForID(req.GroupPk) if err != nil { return nil, errcode.ErrCode_ErrGroupMissing.Wrap(err) } tyberLogGroupContext(ctx, s.logger, gc) op, err := gc.MetadataStore().SendAppMetadata(ctx, req.Payload) if err != nil { return nil, errcode.ErrCode_ErrOrbitDBAppend.Wrap(err) } return &protocoltypes.AppMetadataSend_Reply{Cid: op.GetEntry().GetHash().Bytes()}, nil } func (s *service) AppMessageSend(ctx context.Context, req *protocoltypes.AppMessageSend_Request) (_ *protocoltypes.AppMessageSend_Reply, err error) { ctx, _, endSection := tyber.Section(ctx, s.logger, fmt.Sprintf("Sending message to group %s", base64.RawURLEncoding.EncodeToString(req.GroupPk))) defer func() { endSection(err, "") }() gc, err := s.GetContextGroupForID(req.GroupPk) if err != nil { return nil, errcode.ErrCode_ErrGroupMissing.Wrap(err) } tyberLogGroupContext(ctx, s.logger, gc) op, err := gc.MessageStore().AddMessage(ctx, req.Payload) if err != nil { return nil, errcode.ErrCode_ErrOrbitDBAppend.Wrap(err) } return &protocoltypes.AppMessageSend_Reply{Cid: op.GetEntry().GetHash().Bytes()}, nil } // OutOfStoreReceive parses a payload received outside a synchronized store func (s *service) OutOfStoreReceive(ctx context.Context, request *protocoltypes.OutOfStoreReceive_Request) (*protocoltypes.OutOfStoreReceive_Reply, error) { outOfStoreMessage, group, clearPayload, alreadyDecrypted, err := s.secretStore.OpenOutOfStoreMessage(ctx, request.Payload) if err != nil { return nil, errcode.ErrCode_ErrCryptoDecrypt.Wrap(err) } return &protocoltypes.OutOfStoreReceive_Reply{ Message: outOfStoreMessage, Cleartext: clearPayload, GroupPublicKey: group.PublicKey, AlreadyReceived: alreadyDecrypted, }, nil } // OutOfStoreSeal creates a payload of a message present in store to be sent outside a synchronized store func (s *service) OutOfStoreSeal(ctx context.Context, request *protocoltypes.OutOfStoreSeal_Request) (*protocoltypes.OutOfStoreSeal_Reply, error) { gc, err := s.GetContextGroupForID(request.GroupPublicKey) if err != nil { return nil, err } _, c, err := cid.CidFromBytes(request.Cid) if err != nil { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(err) } sealedMessageEnvelope, err := gc.messageStore.GetOutOfStoreMessageEnvelope(ctx, c) if err != nil { return nil, errcode.ErrCode_ErrInternal.Wrap(err) } sealedMessageEnvelopeBytes, err := proto.Marshal(sealedMessageEnvelope) if err != nil { return nil, errcode.ErrCode_ErrSerialization.Wrap(err) } return &protocoltypes.OutOfStoreSeal_Reply{ Encrypted: sealedMessageEnvelopeBytes, }, nil } func tyberLogGroupContext(ctx context.Context, logger *zap.Logger, gc *GroupContext) { memberPK, err := gc.MemberPubKey().Raw() if err != nil { memberPK = []byte{} } logger.Debug("Got group context", tyber.FormatStepLogFields(ctx, []tyber.Detail{ {Name: "GroupType", Description: gc.Group().GetGroupType().String()}, {Name: "GroupPK", Description: base64.RawURLEncoding.EncodeToString(gc.Group().PublicKey)}, {Name: "MemberPK", Description: base64.RawURLEncoding.EncodeToString(memberPK)}, })...) } ================================================ FILE: api_client.go ================================================ package weshnet import ( "context" "io" "sync" "berty.tech/weshnet/v2/pkg/errcode" "berty.tech/weshnet/v2/pkg/protocoltypes" "berty.tech/weshnet/v2/pkg/tyber" ) func (s *service) ServiceExportData(_ *protocoltypes.ServiceExportData_Request, server protocoltypes.ProtocolService_ServiceExportDataServer) (err error) { ctx, _, endSection := tyber.Section(server.Context(), s.logger, "Exporting protocol instance data") defer func() { endSection(err, "") }() r, w := io.Pipe() var exportErr error wg := sync.WaitGroup{} wg.Add(1) go func() { defer func() { _ = r.Close() }() defer wg.Done() for { contents := make([]byte, 4096) l, err := r.Read(contents) if err == io.EOF { break } else if err != nil { exportErr = errcode.ErrCode_ErrStreamRead.Wrap(err) break } if err := server.Send(&protocoltypes.ServiceExportData_Reply{ExportedData: contents[:l]}); err != nil { exportErr = errcode.ErrCode_ErrStreamWrite.Wrap(err) break } } }() if err := s.export(ctx, w); err != nil { return errcode.ErrCode_ErrInternal.Wrap(err) } _ = w.Close() wg.Wait() if exportErr != nil { return exportErr } return nil } func (s *service) ServiceGetConfiguration(ctx context.Context, _ *protocoltypes.ServiceGetConfiguration_Request) (*protocoltypes.ServiceGetConfiguration_Reply, error) { key, err := s.ipfsCoreAPI.Key().Self(ctx) if err != nil { return nil, errcode.ErrCode_TODO.Wrap(err) } maddrs, err := s.ipfsCoreAPI.Swarm().ListenAddrs(ctx) if err != nil { return nil, errcode.ErrCode_TODO.Wrap(err) } listeners := make([]string, len(maddrs)) for i, addr := range maddrs { listeners[i] = addr.String() } accountGroup := s.getAccountGroup() if accountGroup == nil { return nil, errcode.ErrCode_ErrGroupMissing } member, err := accountGroup.MemberPubKey().Raw() if err != nil { return nil, errcode.ErrCode_ErrSerialization.Wrap(err) } device, err := accountGroup.DevicePubKey().Raw() if err != nil { return nil, errcode.ErrCode_ErrSerialization.Wrap(err) } return &protocoltypes.ServiceGetConfiguration_Reply{ AccountPk: member, DevicePk: device, AccountGroupPk: accountGroup.Group().PublicKey, PeerId: key.ID().String(), Listeners: listeners, }, nil } ================================================ FILE: api_contact.go ================================================ package weshnet import ( "context" "fmt" "time" "github.com/libp2p/go-libp2p/core/crypto" "berty.tech/weshnet/v2/pkg/errcode" "berty.tech/weshnet/v2/pkg/protocoltypes" "berty.tech/weshnet/v2/pkg/tyber" ) func (s *service) ContactAliasKeySend(ctx context.Context, req *protocoltypes.ContactAliasKeySend_Request) (_ *protocoltypes.ContactAliasKeySend_Reply, err error) { ctx, _, endSection := tyber.Section(ctx, s.logger, "Sending contact alias key") defer func() { endSection(err, "") }() g, err := s.GetContextGroupForID(req.GroupPk) if err != nil { return nil, errcode.ErrCode_ErrGroupMissing.Wrap(err) } if _, err := g.MetadataStore().ContactSendAliasKey(ctx); err != nil { return nil, errcode.ErrCode_ErrOrbitDBAppend.Wrap(err) } return &protocoltypes.ContactAliasKeySend_Reply{}, nil } func (s *service) ContactBlock(ctx context.Context, req *protocoltypes.ContactBlock_Request) (_ *protocoltypes.ContactBlock_Reply, err error) { ctx, _, endSection := tyber.Section(ctx, s.logger, "Blocking contact") defer func() { endSection(err, "") }() pk, err := crypto.UnmarshalEd25519PublicKey(req.ContactPk) if err != nil { return nil, errcode.ErrCode_ErrDeserialization.Wrap(err) } if _, err := s.getAccountGroup().MetadataStore().ContactBlock(ctx, pk); err != nil { return nil, errcode.ErrCode_ErrOrbitDBAppend.Wrap(err) } return &protocoltypes.ContactBlock_Reply{}, nil } func (s *service) ContactUnblock(ctx context.Context, req *protocoltypes.ContactUnblock_Request) (_ *protocoltypes.ContactUnblock_Reply, err error) { ctx, _, endSection := tyber.Section(ctx, s.logger, "Unblocking contact") defer func() { endSection(err, "") }() pk, err := crypto.UnmarshalEd25519PublicKey(req.ContactPk) if err != nil { return nil, errcode.ErrCode_ErrDeserialization.Wrap(err) } if _, err := s.getAccountGroup().MetadataStore().ContactUnblock(ctx, pk); err != nil { return nil, errcode.ErrCode_ErrOrbitDBAppend.Wrap(err) } return &protocoltypes.ContactUnblock_Reply{}, nil } func (s *service) RefreshContactRequest(ctx context.Context, req *protocoltypes.RefreshContactRequest_Request) (*protocoltypes.RefreshContactRequest_Reply, error) { if len(req.ContactPk) == 0 { return nil, errcode.ErrCode_ErrInternal } var cancel context.CancelFunc if req.Timeout > 0 { ctx, cancel = context.WithTimeout(ctx, time.Duration(req.Timeout)*time.Second) } else { ctx, cancel = context.WithCancel(ctx) } defer cancel() key := string(req.ContactPk) s.muRefreshprocess.Lock() if clfn, ok := s.refreshprocess[key]; ok { clfn() // close previous refresh method } s.refreshprocess[key] = cancel s.muRefreshprocess.Unlock() peers, err := s.swiper.RefreshContactRequest(ctx, req.ContactPk) if err != nil { return nil, fmt.Errorf("unable to refresh group: %w", err) } res := &protocoltypes.RefreshContactRequest_Reply{ PeersFound: []*protocoltypes.RefreshContactRequest_Peer{}, } for _, p := range peers { // check if we can connect to this peers if err := s.host.Connect(ctx, p); err != nil { continue } addrs := make([]string, len(p.Addrs)) for i, addr := range p.Addrs { addrs[i] = addr.String() } res.PeersFound = append(res.PeersFound, &protocoltypes.RefreshContactRequest_Peer{ Id: p.ID.String(), Addrs: addrs, }) } return res, nil } ================================================ FILE: api_contact_request_test.go ================================================ package weshnet import ( "context" "testing" "time" mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" "github.com/stretchr/testify/require" "berty.tech/weshnet/v2/pkg/protocoltypes" "berty.tech/weshnet/v2/pkg/testutil" ) func TestShareContact(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*20) defer cancel() logger, cleanup := testutil.Logger(t) defer cleanup() opts := TestingOpts{ Mocknet: mocknet.New(), Logger: logger, } pts, cleanup := NewTestingProtocolWithMockedPeers(ctx, t, &opts, nil, 2) defer cleanup() binaryContact, err := pts[0].Client.ShareContact(ctx, &protocoltypes.ShareContact_Request{}) require.NoError(t, err) // Check that ShareContact reset the contact request reference and enabled contact requests. contactRequestRef, err := pts[0].Client.ContactRequestReference(ctx, &protocoltypes.ContactRequestReference_Request{}) require.NoError(t, err) require.NotEqual(t, 0, len(contactRequestRef.PublicRendezvousSeed)) require.Equal(t, true, contactRequestRef.Enabled) // Decode. contact, err := pts[0].Client.DecodeContact(ctx, &protocoltypes.DecodeContact_Request{ EncodedContact: binaryContact.EncodedContact, }) require.NoError(t, err) // Check for the expected info. config, err := pts[0].Client.ServiceGetConfiguration(ctx, &protocoltypes.ServiceGetConfiguration_Request{}) require.NoError(t, err) require.Equal(t, contact.Contact.Pk, config.AccountPk) require.Equal(t, contact.Contact.PublicRendezvousSeed, contactRequestRef.PublicRendezvousSeed) } ================================================ FILE: api_contactrequest.go ================================================ package weshnet import ( "context" "github.com/libp2p/go-libp2p/core/crypto" "google.golang.org/protobuf/proto" "berty.tech/weshnet/v2/pkg/errcode" "berty.tech/weshnet/v2/pkg/protocoltypes" "berty.tech/weshnet/v2/pkg/tyber" ) // ContactRequestReference retrieves the necessary information to create a contact link func (s *service) ContactRequestReference(context.Context, *protocoltypes.ContactRequestReference_Request) (*protocoltypes.ContactRequestReference_Reply, error) { accountGroup := s.getAccountGroup() if accountGroup == nil { return nil, errcode.ErrCode_ErrGroupMissing } enabled, shareableContact := accountGroup.MetadataStore().GetIncomingContactRequestsStatus() rdvSeed := []byte(nil) if shareableContact != nil { rdvSeed = shareableContact.PublicRendezvousSeed } return &protocoltypes.ContactRequestReference_Reply{ PublicRendezvousSeed: rdvSeed, Enabled: enabled, }, nil } // ContactRequestDisable disables incoming contact requests func (s *service) ContactRequestDisable(ctx context.Context, _ *protocoltypes.ContactRequestDisable_Request) (_ *protocoltypes.ContactRequestDisable_Reply, err error) { ctx, _, endSection := tyber.Section(ctx, s.logger, "Disabling contact requests") defer func() { endSection(err, "") }() accountGroup := s.getAccountGroup() if accountGroup == nil { return nil, errcode.ErrCode_ErrGroupMissing } if _, err := accountGroup.MetadataStore().ContactRequestDisable(ctx); err != nil { return nil, errcode.ErrCode_ErrOrbitDBAppend.Wrap(err) } return &protocoltypes.ContactRequestDisable_Reply{}, nil } // ContactRequestEnable enables incoming contact requests func (s *service) ContactRequestEnable(ctx context.Context, _ *protocoltypes.ContactRequestEnable_Request) (_ *protocoltypes.ContactRequestEnable_Reply, err error) { ctx, _, endSection := tyber.Section(ctx, s.logger, "Enabling contact requests") defer func() { endSection(err, "") }() accountGroup := s.getAccountGroup() if accountGroup == nil { return nil, errcode.ErrCode_ErrGroupMissing } if _, err := accountGroup.MetadataStore().ContactRequestEnable(ctx); err != nil { return nil, errcode.ErrCode_ErrOrbitDBAppend.Wrap(err) } _, shareableContact := accountGroup.MetadataStore().GetIncomingContactRequestsStatus() rdvSeed := []byte(nil) if shareableContact != nil { rdvSeed = shareableContact.PublicRendezvousSeed } return &protocoltypes.ContactRequestEnable_Reply{ PublicRendezvousSeed: rdvSeed, }, nil } // ContactRequestResetReference generates a new contact request reference func (s *service) ContactRequestResetReference(ctx context.Context, _ *protocoltypes.ContactRequestResetReference_Request) (_ *protocoltypes.ContactRequestResetReference_Reply, err error) { ctx, _, endSection := tyber.Section(ctx, s.logger, "Resetting contact requests reference") defer func() { endSection(err, "") }() accountGroup := s.getAccountGroup() if accountGroup == nil { return nil, errcode.ErrCode_ErrGroupMissing } if _, err := accountGroup.MetadataStore().ContactRequestReferenceReset(ctx); err != nil { return nil, errcode.ErrCode_ErrOrbitDBAppend.Wrap(err) } _, shareableContact := accountGroup.MetadataStore().GetIncomingContactRequestsStatus() rdvSeed := []byte(nil) if shareableContact != nil { rdvSeed = shareableContact.PublicRendezvousSeed } return &protocoltypes.ContactRequestResetReference_Reply{ PublicRendezvousSeed: rdvSeed, }, nil } // ContactRequestSend enqueues a new contact request to be sent func (s *service) ContactRequestSend(ctx context.Context, req *protocoltypes.ContactRequestSend_Request) (_ *protocoltypes.ContactRequestSend_Reply, err error) { ctx, _, endSection := tyber.Section(ctx, s.logger, "Sending contact request") defer func() { endSection(err, "") }() s.logger.Debug("Contact request info", tyber.FormatStepLogFields(ctx, []tyber.Detail{}, tyber.WithJSONDetail("Request", req))...) shareableContact := req.Contact if shareableContact == nil { return nil, errcode.ErrCode_ErrInvalidInput } accountGroup := s.getAccountGroup() if accountGroup == nil { return nil, errcode.ErrCode_ErrGroupMissing } if _, err := accountGroup.MetadataStore().ContactRequestOutgoingEnqueue(ctx, shareableContact, req.OwnMetadata); err != nil { return nil, errcode.ErrCode_ErrOrbitDBAppend.Wrap(err) } return &protocoltypes.ContactRequestSend_Reply{}, nil } // ContactRequestAccept accepts a contact request func (s *service) ContactRequestAccept(ctx context.Context, req *protocoltypes.ContactRequestAccept_Request) (_ *protocoltypes.ContactRequestAccept_Reply, err error) { ctx, _, endSection := tyber.Section(ctx, s.logger, "Accepting contact request") defer func() { endSection(err, "") }() pk, err := crypto.UnmarshalEd25519PublicKey(req.ContactPk) if err != nil { return nil, errcode.ErrCode_ErrDeserialization.Wrap(err) } group, err := s.secretStore.GetGroupForContact(pk) if err != nil { return nil, errcode.ErrCode_ErrInternal.Wrap(err) } accountGroup := s.getAccountGroup() if accountGroup == nil { return nil, errcode.ErrCode_ErrGroupMissing } if _, err := accountGroup.MetadataStore().ContactRequestIncomingAccept(ctx, pk); err != nil { return nil, errcode.ErrCode_ErrOrbitDBAppend.Wrap(err) } if err = s.secretStore.PutGroup(ctx, group); err != nil { return nil, err } return &protocoltypes.ContactRequestAccept_Reply{}, nil } // ContactRequestDiscard ignores a contact request without informing the request sender func (s *service) ContactRequestDiscard(ctx context.Context, req *protocoltypes.ContactRequestDiscard_Request) (_ *protocoltypes.ContactRequestDiscard_Reply, err error) { ctx, _, endSection := tyber.Section(ctx, s.logger, "Discarding contact request") defer func() { endSection(err, "") }() pk, err := crypto.UnmarshalEd25519PublicKey(req.ContactPk) if err != nil { return nil, errcode.ErrCode_ErrDeserialization.Wrap(err) } accountGroup := s.getAccountGroup() if accountGroup == nil { return nil, errcode.ErrCode_ErrGroupMissing } if _, err := accountGroup.MetadataStore().ContactRequestIncomingDiscard(ctx, pk); err != nil { return nil, errcode.ErrCode_ErrOrbitDBAppend.Wrap(err) } return &protocoltypes.ContactRequestDiscard_Reply{}, nil } // ShareContact uses ContactRequestReference to get the contact information for the current account and // returns the Protobuf encoding which you can further encode and share. If needed, his will reset the // contact request reference and enable contact requests. func (s *service) ShareContact(ctx context.Context, _ *protocoltypes.ShareContact_Request) (_ *protocoltypes.ShareContact_Reply, err error) { accountGroup := s.getAccountGroup() if accountGroup == nil { return nil, errcode.ErrCode_ErrGroupMissing } enabled, shareableContact := accountGroup.MetadataStore().GetIncomingContactRequestsStatus() rdvSeed := []byte(nil) if shareableContact != nil { rdvSeed = shareableContact.PublicRendezvousSeed } if !enabled || len(rdvSeed) == 0 { // We need to enable and reset the contact request reference. if _, err := accountGroup.MetadataStore().ContactRequestEnable(ctx); err != nil { return nil, errcode.ErrCode_ErrOrbitDBAppend.Wrap(err) } if _, err := accountGroup.MetadataStore().ContactRequestReferenceReset(ctx); err != nil { return nil, errcode.ErrCode_ErrOrbitDBAppend.Wrap(err) } // Refresh the info. _, shareableContact = accountGroup.MetadataStore().GetIncomingContactRequestsStatus() rdvSeed = []byte(nil) if shareableContact != nil { rdvSeed = shareableContact.PublicRendezvousSeed } } // Get the client's AccountPK. member, err := accountGroup.MemberPubKey().Raw() if err != nil { return nil, errcode.ErrCode_ErrSerialization.Wrap(err) } encodedContact, err := proto.Marshal(&protocoltypes.ShareableContact{ Pk: member, PublicRendezvousSeed: rdvSeed, }) if err != nil { return nil, err } return &protocoltypes.ShareContact_Reply{ EncodedContact: encodedContact, }, nil } // DecodeContact decodes the Protobuf encoding of a shareable contact which was returned by ShareContact. func (s *service) DecodeContact(_ context.Context, req *protocoltypes.DecodeContact_Request) (_ *protocoltypes.DecodeContact_Reply, err error) { contact := &protocoltypes.ShareableContact{} if err := proto.Unmarshal(req.EncodedContact, contact); err != nil { panic(err) } return &protocoltypes.DecodeContact_Reply{ Contact: contact, }, nil } ================================================ FILE: api_debug.go ================================================ package weshnet import ( "context" "fmt" "strings" "time" "github.com/libp2p/go-libp2p/core/crypto" "github.com/libp2p/go-libp2p/core/network" peer "github.com/libp2p/go-libp2p/core/peer" "go.uber.org/multierr" "go.uber.org/zap" "google.golang.org/protobuf/proto" "berty.tech/go-orbit-db/stores/operation" "berty.tech/weshnet/v2/internal/sysutil" "berty.tech/weshnet/v2/pkg/errcode" "berty.tech/weshnet/v2/pkg/protocoltypes" ) func (s *service) DebugListGroups(_ *protocoltypes.DebugListGroups_Request, srv protocoltypes.ProtocolService_DebugListGroupsServer) error { accountGroup := s.getAccountGroup() if accountGroup == nil { return errcode.ErrCode_ErrGroupMissing } if err := srv.SendMsg(&protocoltypes.DebugListGroups_Reply{ GroupPk: accountGroup.group.PublicKey, GroupType: accountGroup.group.GroupType, }); err != nil { return err } for _, c := range accountGroup.MetadataStore().ListContactsByStatus(protocoltypes.ContactState_ContactStateAdded) { pk, err := crypto.UnmarshalEd25519PublicKey(c.Pk) if err != nil { return errcode.ErrCode_ErrDeserialization.Wrap(err) } group, err := s.secretStore.GetGroupForContact(pk) if err != nil { return errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err) } if err := srv.SendMsg(&protocoltypes.DebugListGroups_Reply{ GroupPk: group.PublicKey, GroupType: group.GroupType, ContactPk: c.Pk, }); err != nil { return err } } for _, g := range accountGroup.MetadataStore().ListMultiMemberGroups() { if err := srv.SendMsg(&protocoltypes.DebugListGroups_Reply{ GroupPk: g.PublicKey, GroupType: g.GroupType, }); err != nil { return err } } return nil } func (s *service) DebugInspectGroupStore(req *protocoltypes.DebugInspectGroupStore_Request, srv protocoltypes.ProtocolService_DebugInspectGroupStoreServer) error { if req.LogType == protocoltypes.DebugInspectGroupLogType_DebugInspectGroupLogTypeUndefined { return errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("invalid log type specified")) } cg, err := s.GetContextGroupForID(req.GroupPk) if err != nil { return errcode.ErrCode_ErrInvalidInput.Wrap(err) } switch req.LogType { case protocoltypes.DebugInspectGroupLogType_DebugInspectGroupLogTypeMessage: for _, e := range cg.messageStore.OpLog().GetEntries().Slice() { var ( payload = []byte(nil) devicePK = []byte(nil) nexts = make([][]byte, len(e.GetNext())) ) if evt, err := cg.messageStore.openMessage(srv.Context(), e); err != nil { s.logger.Error("unable to open message", zap.Error(err)) } else { devicePK = evt.Headers.DevicePk payload = evt.Message } for i, n := range e.GetNext() { nexts[i] = n.Bytes() } if err := srv.SendMsg(&protocoltypes.DebugInspectGroupStore_Reply{ Cid: e.GetHash().Bytes(), ParentCids: nexts, DevicePk: devicePK, Payload: payload, }); err != nil { return err } } case protocoltypes.DebugInspectGroupLogType_DebugInspectGroupLogTypeMetadata: log := cg.metadataStore.OpLog() for _, e := range log.GetEntries().Slice() { var ( eventType protocoltypes.EventType payload = []byte(nil) devicePK = []byte(nil) nexts = make([][]byte, len(e.GetNext())) ) if op, err := operation.ParseOperation(e); err != nil { s.logger.Error("unable to parse operation", zap.Error(err)) } else if meta, event, err := openGroupEnvelope(cg.group, op.GetValue()); err != nil { s.logger.Error("unable to open group envelope", zap.Error(err)) } else if metaEvent, err := newGroupMetadataEventFromEntry(log, e, meta, event, cg.group); err != nil { s.logger.Error("unable to get group metadata event from entry", zap.Error(err)) } else { payload = metaEvent.Event eventType = metaEvent.Metadata.EventType if typeData, ok := eventTypesMapper[metaEvent.Metadata.EventType]; ok { p := proto.Clone(typeData.Message) if err := proto.Unmarshal(metaEvent.Event, p); err == nil { if msg, ok := p.(eventDeviceSigned); ok { devicePK = msg.GetDevicePk() } } } else { s.logger.Error("unable to get message struct for event type", zap.String("event_type", metaEvent.Metadata.EventType.String())) } } for i, n := range e.GetNext() { nexts[i] = n.Bytes() } if err := srv.SendMsg(&protocoltypes.DebugInspectGroupStore_Reply{ Cid: e.GetHash().Bytes(), ParentCids: nexts, Payload: payload, MetadataEventType: eventType, DevicePk: devicePK, }); err != nil { return err } } } return nil } func (s *service) DebugGroup(ctx context.Context, request *protocoltypes.DebugGroup_Request) (*protocoltypes.DebugGroup_Reply, error) { rep := &protocoltypes.DebugGroup_Reply{} peers, err := s.ipfsCoreAPI.Swarm().Peers(ctx) if err != nil { return nil, err } topic := fmt.Sprintf("grp_%s", string(request.GroupPk)) for _, p := range peers { tagInfo := s.ipfsCoreAPI.ConnMgr().GetTagInfo(p.ID()) if _, ok := tagInfo.Tags[topic]; ok { rep.PeerIds = append(rep.PeerIds, p.ID().String()) } } return rep, nil } func (s *service) SystemInfo(ctx context.Context, _ *protocoltypes.SystemInfo_Request) (*protocoltypes.SystemInfo_Reply, error) { reply := protocoltypes.SystemInfo_Reply{} // process process, errs := sysutil.SystemInfoProcess() reply.Process = process reply.Process.StartedAt = s.startedAt.Unix() reply.Process.UptimeMs = time.Since(s.startedAt).Milliseconds() // gRPC // TODO // p2p { reply.P2P = &protocoltypes.SystemInfo_P2P{} // swarm metrics if api := s.IpfsCoreAPI(); api != nil { peers, err := api.Swarm().Peers(ctx) reply.P2P.ConnectedPeers = int64(len(peers)) errs = multierr.Append(errs, err) } else { errs = multierr.Append(errs, fmt.Errorf("no such IPFS core API")) } // pubsub metrics // TODO // BLE metrics } // OrbitDB accountGroup := s.getAccountGroup() if accountGroup == nil { return nil, errcode.ErrCode_ErrGroupMissing } status := accountGroup.metadataStore.ReplicationStatus() reply.Orbitdb = &protocoltypes.SystemInfo_OrbitDB{ AccountMetadata: &protocoltypes.SystemInfo_OrbitDB_ReplicationStatus{ Progress: int64(status.GetProgress()), Maximum: int64(status.GetMax()), }, } // FIXME: compute more stores // warns if errs != nil { reply.Warns = []string{} for _, err := range multierr.Errors(errs) { reply.Warns = append(reply.Warns, err.Error()) } } return &reply, nil } func (s *service) PeerList(ctx context.Context, _ *protocoltypes.PeerList_Request) (*protocoltypes.PeerList_Reply, error) { reply := protocoltypes.PeerList_Reply{} api := s.IpfsCoreAPI() if api == nil { return nil, errcode.ErrCode_TODO.Wrap(fmt.Errorf("IPFS Core API is not available")) } swarmPeers, err := api.Swarm().Peers(ctx) // https://pkg.go.dev/github.com/ipfs/interface-go-ipfs-core#ConnectionInfo if err != nil { return nil, errcode.ErrCode_TODO.Wrap(err) } peers := map[peer.ID]*protocoltypes.PeerList_Peer{} // each peer in the swarm should be visible for _, swarmPeer := range swarmPeers { peers[swarmPeer.ID()] = &protocoltypes.PeerList_Peer{ Id: swarmPeer.ID().String(), Errors: []string{}, Routes: []*protocoltypes.PeerList_Route{}, } } // FIXME: do not restrict on swarm peers, also print some other important ones (old, etc) // append peer addrs from peerstore for peerID, peer := range peers { info := s.host.Peerstore().PeerInfo(peerID) for _, addr := range info.Addrs { peer.Routes = append(peer.Routes, &protocoltypes.PeerList_Route{ Address: addr.String(), }) } } // append more info for active connections for _, swarmPeer := range swarmPeers { peer, ok := peers[swarmPeer.ID()] if !ok { peer = &protocoltypes.PeerList_Peer{ Id: swarmPeer.ID().String(), Errors: []string{}, Routes: []*protocoltypes.PeerList_Route{}, } peer.Errors = append(peer.Errors, "peer in swarm peers, but not in peerstore") peers[swarmPeer.ID()] = peer } address := swarmPeer.Address().String() found := false var selectedRoute *protocoltypes.PeerList_Route for _, route := range peer.Routes { if route.Address == address { found = true selectedRoute = route } } if !found { newRoute := protocoltypes.PeerList_Route{Address: address} peer.Routes = append(peer.Routes, &newRoute) selectedRoute = &newRoute } selectedRoute.IsActive = true // latency { latency, err := swarmPeer.Latency() if err != nil { peer.Errors = append(peer.Errors, err.Error()) } else { selectedRoute.Latency = latency.Milliseconds() } } // direction { switch swarmPeer.Direction() { case network.DirInbound: selectedRoute.Direction = protocoltypes.Direction_InboundDir case network.DirOutbound: selectedRoute.Direction = protocoltypes.Direction_OutboundDir } } // streams { peerStreams, err := swarmPeer.Streams() if err != nil { peer.Errors = append(peer.Errors, err.Error()) } else { selectedRoute.Streams = []*protocoltypes.PeerList_Stream{} for _, peerStream := range peerStreams { if peerStream == "" { continue } selectedRoute.Streams = append(selectedRoute.Streams, &protocoltypes.PeerList_Stream{ Id: string(peerStream), }) } } } } // compute features for _, peer := range peers { features := map[protocoltypes.PeerList_Feature]bool{} for _, route := range peer.Routes { // FIXME: use the multiaddr library instead of string comparisons if strings.Contains(route.Address, "/quic") { features[protocoltypes.PeerList_QuicFeature] = true } if strings.Contains(route.Address, "/mc/") { features[protocoltypes.PeerList_BLEFeature] = true features[protocoltypes.PeerList_WeshFeature] = true } if strings.Contains(route.Address, "/tor/") { features[protocoltypes.PeerList_TorFeature] = true } for _, stream := range route.Streams { if stream.Id == "/wesh/contact_req/1.0.0" { features[protocoltypes.PeerList_WeshFeature] = true } if stream.Id == "/rendezvous/1.0.0" { features[protocoltypes.PeerList_WeshFeature] = true } } } for feature := range features { peer.Features = append(peer.Features, feature) } } // compute peer-level aggregates for _, peer := range peers { // aggregate direction for _, route := range peer.Routes { if route.Direction == protocoltypes.Direction_UnknownDir { continue } switch { case peer.Direction == protocoltypes.Direction_UnknownDir: // first route with a direction peer.Direction = route.Direction case peer.Direction == protocoltypes.Direction_BiDir: // peer aggregate is already maximal // noop case route.Direction == peer.Direction: // another route with the same direction // noop case route.Direction == protocoltypes.Direction_InboundDir && peer.Direction == protocoltypes.Direction_OutboundDir: peer.Direction = protocoltypes.Direction_BiDir case route.Direction == protocoltypes.Direction_OutboundDir && peer.Direction == protocoltypes.Direction_InboundDir: peer.Direction = protocoltypes.Direction_BiDir default: peer.Errors = append(peer.Errors, "failed to compute direction aggregate") } } // aggregate latency for _, route := range peer.Routes { if route.Latency == 0 { continue } switch { case peer.MinLatency == 0: // first route with a latency peer.MinLatency = route.Latency case peer.MinLatency > route.Latency: // smaller value peer.MinLatency = route.Latency } } // aggregate isActive for _, route := range peer.Routes { if route.IsActive { peer.IsActive = true break } } } // FIXME: compute pubsub peers too? // FIXME: add metrics about "amount of times seen", "first time seen", "bandwidth" // use protobuf format for _, peer := range peers { reply.Peers = append(reply.Peers, peer) } return &reply, nil } ================================================ FILE: api_event.go ================================================ package weshnet import ( "context" "errors" "fmt" "github.com/libp2p/go-libp2p/p2p/host/eventbus" "berty.tech/weshnet/v2/pkg/errcode" "berty.tech/weshnet/v2/pkg/protocoltypes" ) func checkParametersConsistency(sinceID, untilID []byte, sinceNow, untilNow, reverseOrder bool) error { // Since can't be both set to an ID and to now if sinceID != nil && sinceNow { return errcode.ErrCode_ErrInvalidInput.Wrap(errors.New("params SinceNow and SinceID are both set")) } // Until can't be both set to an ID and to now if untilID != nil && untilNow { return errcode.ErrCode_ErrInvalidInput.Wrap(errors.New("params UntilNow and UntilID are both set")) } // Since and Until can't be both set to now at the same time if sinceNow && untilNow { return errcode.ErrCode_ErrInvalidInput.Wrap(errors.New("params SinceNow and UntilNow are both set")) } // Can't reverse events orders if subscribed to new events if untilID == nil && !untilNow && reverseOrder { return errcode.ErrCode_ErrInvalidInput.Wrap(errors.New("reverse chronological order requested while subscribing to new events")) } return nil } // GroupMetadataList replays previous and subscribes to new metadata events from the group func (s *service) GroupMetadataList(req *protocoltypes.GroupMetadataList_Request, sub protocoltypes.ProtocolService_GroupMetadataListServer) error { ctx, cancel := context.WithCancel(sub.Context()) defer cancel() // Get group context / check if the group is opened cg, err := s.GetContextGroupForID(req.GroupPk) if err != nil { return errcode.ErrCode_ErrGroupMemberUnknownGroupID.Wrap(err) } // Check parameters consistency if err := checkParametersConsistency(req.SinceId, req.UntilId, req.SinceNow, req.UntilNow, req.ReverseOrder); err != nil { return err } // Subscribe to new metadata events if requested var newEvents <-chan any if req.UntilId == nil && !req.UntilNow { sub, err := cg.MetadataStore().EventBus().Subscribe([]any{ // new(stores.EventReplicated), new(*protocoltypes.GroupMetadataEvent), }, eventbus.Name("weshnet/api/group-metadata-list"), eventbus.BufSize(32)) if err != nil { return fmt.Errorf("unable to subscribe to new events") } defer sub.Close() newEvents = sub.Out() } // Subscribe to previous metadata events and stream them if requested previousEvents := make(chan *protocoltypes.GroupMetadataEvent) if !req.SinceNow { pevt, err := cg.MetadataStore().ListEvents(ctx, req.SinceId, req.UntilId, req.ReverseOrder) if err != nil { return err } go func() { for { var evt *protocoltypes.GroupMetadataEvent select { case <-ctx.Done(): return case evt = <-pevt: } if evt == nil { // if we don't want to stream new event, cancel the process if req.UntilNow { cancel() } else { previousEvents <- &protocoltypes.GroupMetadataEvent{EventContext: nil} } cg.logger.Debug("GroupMetadataList: previous events stream ended") return } previousEvents <- evt } }() } // Subscribe to new metadata events and stream them if requested for { var event any select { case <-ctx.Done(): return nil case event = <-previousEvents: case event = <-newEvents: } msg := event.(*protocoltypes.GroupMetadataEvent) if msg.EventContext == nil { continue } if err := sub.Send(msg); err != nil { return err } cg.logger.Info("service - metadata store - sent 1 event from log subscription") } } // GroupMessageList replays previous and subscribes to new message events from the group func (s *service) GroupMessageList(req *protocoltypes.GroupMessageList_Request, sub protocoltypes.ProtocolService_GroupMessageListServer) error { ctx, cancel := context.WithCancel(sub.Context()) defer cancel() // Get group context / check if the group is opened cg, err := s.GetContextGroupForID(req.GroupPk) if err != nil { return errcode.ErrCode_ErrGroupMemberUnknownGroupID.Wrap(err) } // Check parameters consistency if err := checkParametersConsistency(req.SinceId, req.UntilId, req.SinceNow, req.UntilNow, req.ReverseOrder); err != nil { return err } // Subscribe to new message events if requested var newEvents <-chan any if req.UntilId == nil && !req.UntilNow { messageStoreSub, err := cg.MessageStore().EventBus().Subscribe([]any{ new(*protocoltypes.GroupMessageEvent), }, eventbus.Name("weshnet/api/group-message-list")) if err != nil { return fmt.Errorf("unable to subscribe to new events") } defer messageStoreSub.Close() newEvents = messageStoreSub.Out() } // Subscribe to previous message events and stream them if requested previousEvents := make(chan *protocoltypes.GroupMessageEvent) if !req.SinceNow { pevt, err := cg.MessageStore().ListEvents(ctx, req.SinceId, req.UntilId, req.ReverseOrder) if err != nil { return err } go func() { for { var evt *protocoltypes.GroupMessageEvent select { case <-ctx.Done(): return case evt = <-pevt: } if evt == nil { // if we don't want to stream new event, cancel the process if req.UntilNow { cancel() } else { previousEvents <- &protocoltypes.GroupMessageEvent{EventContext: nil} } cg.logger.Debug("GroupMessageList: previous events stream ended") return } previousEvents <- evt } }() } // Subscribe to new message events and stream them if requested for { var event any select { case <-ctx.Done(): return nil case event = <-previousEvents: case event = <-newEvents: } msg := event.(*protocoltypes.GroupMessageEvent) if msg.EventContext == nil { continue } if err := sub.Send(msg); err != nil { return err } cg.logger.Info("service - message store - sent 1 event from log subscription") } } ================================================ FILE: api_group.go ================================================ package weshnet import ( "context" "encoding/base64" "encoding/hex" "fmt" "github.com/libp2p/go-libp2p/core/crypto" peer "github.com/libp2p/go-libp2p/core/peer" manet "github.com/multiformats/go-multiaddr/net" "go.uber.org/zap" "google.golang.org/protobuf/proto" "berty.tech/weshnet/v2/pkg/errcode" "berty.tech/weshnet/v2/pkg/logutil" "berty.tech/weshnet/v2/pkg/protocoltypes" ) func (s *service) GroupInfo(ctx context.Context, req *protocoltypes.GroupInfo_Request) (*protocoltypes.GroupInfo_Reply, error) { var ( g *protocoltypes.Group err error ) switch { case req.GroupPk != nil: pk, err := crypto.UnmarshalEd25519PublicKey(req.GroupPk) if err != nil { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(err) } g, err = s.getGroupForPK(ctx, pk) if err != nil { return nil, errcode.ErrCode_TODO.Wrap(err) } case req.ContactPk != nil: pk, err := crypto.UnmarshalEd25519PublicKey(req.ContactPk) if err != nil { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(err) } g, err = s.getContactGroup(pk) if err != nil { return nil, errcode.ErrCode_ErrOrbitDBOpen.Wrap(err) } default: return nil, errcode.ErrCode_ErrInvalidInput } memberDevice, err := s.secretStore.GetOwnMemberDeviceForGroup(g) if err != nil { return nil, errcode.ErrCode_TODO.Wrap(err) } member, err := memberDevice.Member().Raw() if err != nil { return nil, errcode.ErrCode_ErrSerialization.Wrap(err) } device, err := memberDevice.Device().Raw() if err != nil { return nil, errcode.ErrCode_ErrSerialization.Wrap(err) } return &protocoltypes.GroupInfo_Reply{ Group: g, MemberPk: member, DevicePk: device, }, nil } func (s *service) ActivateGroup(ctx context.Context, req *protocoltypes.ActivateGroup_Request) (*protocoltypes.ActivateGroup_Reply, error) { pk, err := crypto.UnmarshalEd25519PublicKey(req.GroupPk) if err != nil { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(err) } err = s.activateGroup(ctx, pk, req.LocalOnly) if err != nil { return nil, errcode.ErrCode_ErrInternal.Wrap(err) } return &protocoltypes.ActivateGroup_Reply{}, nil } func (s *service) DeactivateGroup(_ context.Context, req *protocoltypes.DeactivateGroup_Request) (*protocoltypes.DeactivateGroup_Reply, error) { pk, err := crypto.UnmarshalEd25519PublicKey(req.GroupPk) if err != nil { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(err) } if err := s.deactivateGroup(pk); err != nil { return nil, errcode.ErrCode_ErrInternal.Wrap(err) } return &protocoltypes.DeactivateGroup_Reply{}, nil } func (s *service) GroupDeviceStatus(req *protocoltypes.GroupDeviceStatus_Request, srv protocoltypes.ProtocolService_GroupDeviceStatusServer) error { ctx := srv.Context() gkey := hex.EncodeToString(req.GroupPk) peers := PeersConnectedness{} logger := s.logger.Named("pstatus") logger.Debug("start monitor device status group", logutil.PrivateString("group_key", gkey)) for { updated, ok := s.peerStatusManager.WaitForConnectednessChange(ctx, gkey, peers) if !ok { return nil // server context has expired } // send updated peers var err error for _, peer := range updated { var evt protocoltypes.GroupDeviceStatus_Reply switch peers[peer] { case ConnectednessTypeConnected: evt.Type = protocoltypes.GroupDeviceStatus_TypePeerConnected var connected *protocoltypes.GroupDeviceStatus_Reply_PeerConnected if connected, err = s.craftPeerConnectedMessage(peer); err == nil { evt.Event, err = proto.Marshal(connected) logger.Debug("peer connected", logutil.PrivateString("group_key", gkey), logutil.PrivateString("peer", connected.PeerId), logutil.PrivateString("devicePK", base64.URLEncoding.EncodeToString(connected.GetDevicePk()))) } case ConnectednessTypeDisconnected: evt.Type = protocoltypes.GroupDeviceStatus_TypePeerDisconnected disconnected := s.craftDeviceDisconnectedMessage(peer) logger.Debug("peer disconnected", logutil.PrivateString("group_key", gkey), logutil.PrivateString("peer", disconnected.PeerId)) evt.Event, err = proto.Marshal(disconnected) case ConnectednessTypeReconnecting: evt.Type = protocoltypes.GroupDeviceStatus_TypePeerConnected reconnecting := s.craftDeviceReconnectedMessage(peer) logger.Debug("peer reconnecting", logutil.PrivateString("group_key", gkey), logutil.PrivateString("peer", reconnecting.PeerId)) evt.Event, err = proto.Marshal(reconnecting) default: evt.Type = protocoltypes.GroupDeviceStatus_TypeUnknown } if err != nil { logger.Error("GroupDeviceStatus: unable to handle event", zap.Error(err)) continue } if err := srv.Send(&evt); err != nil { logger.Debug("GroupDeviceStatus: failed to send event", zap.Error(err)) return err } } } } func (s *service) craftPeerConnectedMessage(peer peer.ID) (*protocoltypes.GroupDeviceStatus_Reply_PeerConnected, error) { pdg, ok := s.odb.GetDevicePKForPeerID(peer) if !ok { return nil, fmt.Errorf("PeerDeviceGroup unknown") } devicePKRaw, err := pdg.DevicePK.Raw() if err != nil { return nil, fmt.Errorf("unable to get raw devicePK: %w", err) } connected := protocoltypes.GroupDeviceStatus_Reply_PeerConnected{ PeerId: peer.String(), DevicePk: devicePKRaw, } activeConns := s.host.Network().ConnsToPeer(peer) connected.Transports = make([]protocoltypes.GroupDeviceStatus_Transport, len(activeConns)) connected.Maddrs = make([]string, len(activeConns)) CONN_LOOP: for i, conn := range activeConns { connected.Maddrs[i] = conn.RemoteMultiaddr().String() // check for proximity transport protocols := conn.RemoteMultiaddr().Protocols() for _, protocol := range protocols { switch protocol.Name { case "nearby", "mc", "ble": connected.Transports[i] = protocoltypes.GroupDeviceStatus_TptProximity continue CONN_LOOP } } // otherwise, check for WAN/LAN addr if manet.IsPrivateAddr(conn.RemoteMultiaddr()) { connected.Transports[i] = protocoltypes.GroupDeviceStatus_TptLAN } else { connected.Transports[i] = protocoltypes.GroupDeviceStatus_TptWAN } } return &connected, nil } func (s *service) craftDeviceDisconnectedMessage(peer peer.ID) *protocoltypes.GroupDeviceStatus_Reply_PeerDisconnected { return &protocoltypes.GroupDeviceStatus_Reply_PeerDisconnected{ PeerId: peer.String(), } } func (s *service) craftDeviceReconnectedMessage(peer peer.ID) *protocoltypes.GroupDeviceStatus_Reply_PeerReconnecting { return &protocoltypes.GroupDeviceStatus_Reply_PeerReconnecting{ PeerId: peer.String(), } } ================================================ FILE: api_multimember.go ================================================ package weshnet import ( "context" "fmt" "github.com/libp2p/go-libp2p/core/crypto" "berty.tech/weshnet/v2/pkg/errcode" "berty.tech/weshnet/v2/pkg/protocoltypes" "berty.tech/weshnet/v2/pkg/tyber" ) // MultiMemberGroupCreate creates a new MultiMember group func (s *service) MultiMemberGroupCreate(ctx context.Context, _ *protocoltypes.MultiMemberGroupCreate_Request) (_ *protocoltypes.MultiMemberGroupCreate_Reply, err error) { ctx, _, endSection := tyber.Section(ctx, s.logger, "Creating MultiMember group") defer func() { endSection(err, "") }() group, groupPrivateKey, err := NewGroupMultiMember() if err != nil { return nil, errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err) } accountGroup := s.getAccountGroup() if accountGroup == nil { return nil, errcode.ErrCode_ErrGroupMissing } _, err = accountGroup.MetadataStore().GroupJoin(ctx, group) if err != nil { return nil, errcode.ErrCode_ErrOrbitDBAppend.Wrap(err) } if err := s.secretStore.PutGroup(ctx, group); err != nil { return nil, errcode.ErrCode_ErrInternal.Wrap(err) } err = s.activateGroup(ctx, groupPrivateKey.GetPublic(), false) if err != nil { return nil, errcode.ErrCode_ErrInternal.Wrap(fmt.Errorf("unable to activate group: %w", err)) } cg, err := s.GetContextGroupForID(group.PublicKey) if err != nil { return nil, errcode.ErrCode_ErrOrbitDBAppend.Wrap(err) } _, err = cg.MetadataStore().ClaimGroupOwnership(ctx, groupPrivateKey) if err != nil { return nil, errcode.ErrCode_ErrOrbitDBAppend.Wrap(err) } return &protocoltypes.MultiMemberGroupCreate_Reply{ GroupPk: group.PublicKey, }, nil } // MultiMemberGroupJoin joins an existing MultiMember group using an invitation func (s *service) MultiMemberGroupJoin(ctx context.Context, req *protocoltypes.MultiMemberGroupJoin_Request) (_ *protocoltypes.MultiMemberGroupJoin_Reply, err error) { ctx, _, endSection := tyber.Section(ctx, s.logger, "Joining MultiMember group") defer func() { endSection(err, "") }() accountGroup := s.getAccountGroup() if accountGroup == nil { return nil, errcode.ErrCode_ErrGroupMissing } if _, err := accountGroup.MetadataStore().GroupJoin(ctx, req.Group); err != nil { return nil, errcode.ErrCode_ErrOrbitDBAppend.Wrap(err) } return &protocoltypes.MultiMemberGroupJoin_Reply{}, nil } // MultiMemberGroupLeave leaves a previously joined MultiMember group func (s *service) MultiMemberGroupLeave(ctx context.Context, req *protocoltypes.MultiMemberGroupLeave_Request) (_ *protocoltypes.MultiMemberGroupLeave_Reply, err error) { ctx, _, endSection := tyber.Section(ctx, s.logger, "Leaving MultiMember group") defer func() { endSection(err, "") }() pk, err := crypto.UnmarshalEd25519PublicKey(req.GroupPk) if err != nil { return nil, errcode.ErrCode_ErrDeserialization.Wrap(err) } accountGroup := s.getAccountGroup() if accountGroup == nil { return nil, errcode.ErrCode_ErrGroupMissing } _, err = accountGroup.MetadataStore().GroupLeave(ctx, pk) if err != nil { return nil, errcode.ErrCode_ErrOrbitDBAppend.Wrap(err) } if err := s.deactivateGroup(pk); err != nil { return nil, errcode.ErrCode_ErrOrbitDBAppend.Wrap(err) } return &protocoltypes.MultiMemberGroupLeave_Reply{}, nil } // MultiMemberGroupAliasResolverDisclose sends an deviceKeystore identity proof to the group members func (s *service) MultiMemberGroupAliasResolverDisclose(ctx context.Context, req *protocoltypes.MultiMemberGroupAliasResolverDisclose_Request) (*protocoltypes.MultiMemberGroupAliasResolverDisclose_Reply, error) { cg, err := s.GetContextGroupForID(req.GroupPk) if err != nil { return nil, errcode.ErrCode_ErrGroupMemberUnknownGroupID.Wrap(err) } _, err = cg.MetadataStore().SendAliasProof(ctx) if err != nil { return nil, errcode.ErrCode_ErrOrbitDBAppend.Wrap(err) } return &protocoltypes.MultiMemberGroupAliasResolverDisclose_Reply{}, nil } // MultiMemberGroupAdminRoleGrant grants admin role to another member of the group func (s *service) MultiMemberGroupAdminRoleGrant(context.Context, *protocoltypes.MultiMemberGroupAdminRoleGrant_Request) (*protocoltypes.MultiMemberGroupAdminRoleGrant_Reply, error) { return nil, errcode.ErrCode_ErrNotImplemented } // MultiMemberGroupInvitationCreate creates a group invitation func (s *service) MultiMemberGroupInvitationCreate(_ context.Context, req *protocoltypes.MultiMemberGroupInvitationCreate_Request) (*protocoltypes.MultiMemberGroupInvitationCreate_Reply, error) { cg, err := s.GetContextGroupForID(req.GroupPk) if err != nil { return nil, errcode.ErrCode_ErrGroupMemberUnknownGroupID.Wrap(err) } return &protocoltypes.MultiMemberGroupInvitationCreate_Reply{ Group: cg.Group(), }, nil } ================================================ FILE: api_replication.go ================================================ package weshnet import ( "context" "crypto/tls" "encoding/base64" "fmt" "go.uber.org/zap" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials/insecure" "berty.tech/weshnet/v2/pkg/errcode" "berty.tech/weshnet/v2/pkg/grpcutil" "berty.tech/weshnet/v2/pkg/logutil" "berty.tech/weshnet/v2/pkg/protocoltypes" "berty.tech/weshnet/v2/pkg/replicationtypes" "berty.tech/weshnet/v2/pkg/tyber" ) func FilterGroupForReplication(m *protocoltypes.Group) (*protocoltypes.Group, error) { groupSigPK, err := m.GetSigningPubKey() if err != nil { return nil, errcode.ErrCode_TODO.Wrap(err) } groupSigPKBytes, err := groupSigPK.Raw() if err != nil { return nil, errcode.ErrCode_ErrSerialization.Wrap(err) } linkKey, err := m.GetLinkKeyArray() if err != nil { return nil, errcode.ErrCode_TODO.Wrap(err) } return &protocoltypes.Group{ PublicKey: m.PublicKey, SignPub: groupSigPKBytes, LinkKey: linkKey[:], LinkKeySig: m.LinkKeySig, }, nil } func (s *service) ReplicationServiceRegisterGroup(ctx context.Context, request *protocoltypes.ReplicationServiceRegisterGroup_Request) (_ *protocoltypes.ReplicationServiceRegisterGroup_Reply, err error) { ctx, _, endSection := tyber.Section(ctx, s.logger, "Registering replication service for group") defer func() { endSection(err, "") }() if request.GroupPk == nil { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("invalid GroupPK")) } if request.Token == "" { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("invalid token")) } if request.ReplicationServer == "" { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("invalid replication server")) } gc, err := s.GetContextGroupForID(request.GroupPk) if err != nil { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(err) } replGroup, err := FilterGroupForReplication(gc.group) if err != nil { return nil, errcode.ErrCode_TODO.Wrap(err) } accountGroup := s.getAccountGroup() if accountGroup == nil { return nil, errcode.ErrCode_ErrGroupMissing } gopts := []grpc.DialOption{ grpc.WithPerRPCCredentials(grpcutil.NewUnsecureSimpleAuthAccess("bearer", request.Token)), } if s.grpcInsecure { gopts = append(gopts, grpc.WithTransportCredentials(insecure.NewCredentials())) } else { tlsconfig := credentials.NewTLS(&tls.Config{ MinVersion: tls.VersionTLS12, }) gopts = append(gopts, grpc.WithTransportCredentials(tlsconfig)) } cc, err := grpc.NewClient("passthrough://"+request.ReplicationServer, gopts...) if err != nil { return nil, errcode.ErrCode_ErrStreamWrite.Wrap(err) } client := replicationtypes.NewReplicationServiceClient(cc) if _, err = client.ReplicateGroup(ctx, &replicationtypes.ReplicationServiceReplicateGroup_Request{ Group: replGroup, }); err != nil { return nil, errcode.ErrCode_ErrServiceReplicationServer.Wrap(err) } s.logger.Info("group will be replicated", logutil.PrivateString("public-key", base64.RawURLEncoding.EncodeToString(request.GroupPk))) if _, err := gc.metadataStore.SendGroupReplicating(ctx, request.AuthenticationUrl, request.ReplicationServer); err != nil { s.logger.Error("error while notifying group about replication", zap.Error(err)) } return &protocoltypes.ReplicationServiceRegisterGroup_Reply{}, nil } ================================================ FILE: api_verified_credentials.go ================================================ package weshnet import ( "bytes" "context" "fmt" "strings" "time" "berty.tech/weshnet/v2/pkg/bertyvcissuer" "berty.tech/weshnet/v2/pkg/cryptoutil" "berty.tech/weshnet/v2/pkg/errcode" "berty.tech/weshnet/v2/pkg/protocoltypes" ) func (s *service) CredentialVerificationServiceInitFlow(ctx context.Context, request *protocoltypes.CredentialVerificationServiceInitFlow_Request) (*protocoltypes.CredentialVerificationServiceInitFlow_Reply, error) { s.lock.Lock() s.vcClient = bertyvcissuer.NewClient(request.ServiceUrl) client := s.vcClient s.lock.Unlock() ctx, cancel := context.WithTimeout(ctx, time.Second*10) defer cancel() // TODO: allow selection of alt-scoped keys // TODO: avoid exporting account keys pkRaw, err := s.accountGroupCtx.ownMemberDevice.Member().Raw() if err != nil { return nil, errcode.ErrCode_ErrInvalidInput } if !bytes.Equal(pkRaw, request.PublicKey) { return nil, errcode.ErrCode_ErrInvalidInput } url, err := client.Init(ctx, request.Link, cryptoutil.NewFuncSigner(s.accountGroupCtx.ownMemberDevice.Member(), s.accountGroupCtx.ownMemberDevice.MemberSign)) if err != nil { return nil, errcode.ErrCode_ErrInternal.Wrap(err) } return &protocoltypes.CredentialVerificationServiceInitFlow_Reply{ Url: url, SecureUrl: strings.HasPrefix(url, "https://"), }, nil } func (s *service) CredentialVerificationServiceCompleteFlow(ctx context.Context, request *protocoltypes.CredentialVerificationServiceCompleteFlow_Request) (*protocoltypes.CredentialVerificationServiceCompleteFlow_Reply, error) { s.lock.Lock() client := s.vcClient s.lock.Unlock() if client == nil { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("a verification flow needs to be started first")) } credentials, identifier, parsedCredential, err := client.Complete(request.CallbackUri) if err != nil { return nil, errcode.ErrCode_ErrInternal.Wrap(err) } _, err = s.accountGroupCtx.metadataStore.SendAccountVerifiedCredentialAdded(ctx, &protocoltypes.AccountVerifiedCredentialRegistered{ VerifiedCredential: credentials, RegistrationDate: parsedCredential.Issued.UnixNano(), ExpirationDate: parsedCredential.Expired.UnixNano(), Identifier: identifier, Issuer: parsedCredential.Issuer.ID, }) if err != nil { return nil, errcode.ErrCode_ErrInternal.Wrap(err) } return &protocoltypes.CredentialVerificationServiceCompleteFlow_Reply{ Identifier: identifier, }, nil } func (s *service) VerifiedCredentialsList(request *protocoltypes.VerifiedCredentialsList_Request, server protocoltypes.ProtocolService_VerifiedCredentialsListServer) error { now := time.Now().UnixNano() credentials := s.accountGroupCtx.metadataStore.ListVerifiedCredentials() for _, credential := range credentials { if request.FilterIdentifier != "" && credential.Identifier != request.FilterIdentifier { continue } if request.ExcludeExpired && credential.ExpirationDate < now { continue } if request.FilterIssuer != "" && credential.Issuer != request.FilterIssuer { continue } if err := server.Send(&protocoltypes.VerifiedCredentialsList_Reply{ Credential: credential, }); err != nil { return errcode.ErrCode_ErrStreamWrite.Wrap(err) } } return nil } ================================================ FILE: blackbox_test.go ================================================ package weshnet_test import ( "context" "fmt" "os" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "berty.tech/weshnet/v2" "berty.tech/weshnet/v2/pkg/protocoltypes" "berty.tech/weshnet/v2/pkg/secretstore" "berty.tech/weshnet/v2/pkg/testutil" ) func TestTestingClient_impl(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() logger, cleanup := testutil.Logger(t) defer cleanup() secretStore, err := secretstore.NewInMemSecretStore(nil) require.NoError(t, err) client, cleanup := weshnet.TestingService(ctx, t, weshnet.Opts{ Logger: logger, SecretStore: secretStore, }) defer cleanup() // test service _, _ = client.ServiceGetConfiguration(ctx, &protocoltypes.ServiceGetConfiguration_Request{}) status := client.Status() expected := weshnet.Status{} assert.Equal(t, expected, status) } func ExampleNewInMemoryServiceClient_basic() { // disable resources manager for test os.Setenv("LIBP2P_RCMGR", "false") ctx, cancel := context.WithCancel(context.Background()) defer cancel() client, err := weshnet.NewInMemoryServiceClient() if err != nil { panic(err) } defer client.Close() ret, err := client.ServiceGetConfiguration(ctx, &protocoltypes.ServiceGetConfiguration_Request{}) if err != nil { panic(err) } for _, listener := range ret.Listeners { if listener == "/p2p-circuit" { fmt.Println(listener) } } // Output: // /p2p-circuit } func ExampleNewPersistentServiceClient_basic() { // disable resources manager for test os.Setenv("LIBP2P_RCMGR", "false") ctx, cancel := context.WithCancel(context.Background()) defer cancel() // create a temporary path to host data of our persistent service path, err := os.MkdirTemp("", "weshnet-test-persistent") if err != nil { panic(err) } defer os.RemoveAll(path) var peerid string // open once { client, err := weshnet.NewPersistentServiceClient(path) if err != nil { panic(err) } ret, err := client.ServiceGetConfiguration(ctx, &protocoltypes.ServiceGetConfiguration_Request{}) if err != nil { panic(err) } peerid = ret.PeerId if err := client.Close(); err != nil { panic(err) } } // open twice { client, err := weshnet.NewPersistentServiceClient(path) if err != nil { panic(err) } defer client.Close() ret, err := client.ServiceGetConfiguration(ctx, &protocoltypes.ServiceGetConfiguration_Request{}) if err != nil { panic(err) } if peerid != ret.PeerId { panic("peerid should be identical") } } // Output: } func ExampleNewServiceClient_basic() { // disable resources manager for test os.Setenv("LIBP2P_RCMGR", "false") ctx, cancel := context.WithCancel(context.Background()) defer cancel() client, err := weshnet.NewServiceClient(weshnet.Opts{}) if err != nil { panic(err) } defer client.Close() ret, err := client.ServiceGetConfiguration(ctx, &protocoltypes.ServiceGetConfiguration_Request{}) if err != nil { panic(err) } for _, listener := range ret.Listeners { if listener == "/p2p-circuit" { fmt.Println(listener) } } // Output: // /p2p-circuit } func ExampleNewService_basic() { // disable resources manager for test os.Setenv("LIBP2P_RCMGR", "false") ctx, cancel := context.WithCancel(context.Background()) defer cancel() client, err := weshnet.NewService(weshnet.Opts{}) if err != nil { panic(err) } defer client.Close() ret, err := client.ServiceGetConfiguration(ctx, &protocoltypes.ServiceGetConfiguration_Request{}) if err != nil { panic(err) } for _, listener := range ret.Listeners { if listener == "/p2p-circuit" { fmt.Println(listener) } } // Output: // /p2p-circuit } // FIXME: create examples that actually use groups and contacts ================================================ FILE: buf.gen.tag.yaml ================================================ version: v1 plugins: - name: gotag out: ./ opt: module=berty.tech/weshnet/v2 ================================================ FILE: buf.gen.yaml ================================================ version: v2 plugins: - local: protoc-gen-go out: ./ opt: module=berty.tech/weshnet/v2 - local: protoc-gen-go-grpc out: ./ opt: module=berty.tech/weshnet/v2 - local: protoc-gen-grpc-gateway out: ./ opt: - module=berty.tech/weshnet/v2 - generate_unbound_methods=true ================================================ FILE: connectedness_manager.go ================================================ package weshnet import ( "context" "sync" peer "github.com/libp2p/go-libp2p/core/peer" "berty.tech/weshnet/v2/internal/notify" ) type ConnectednessType int const ( ConnectednessTypeDisconnected ConnectednessType = iota ConnectednessTypeReconnecting ConnectednessTypeConnected ) type ConnectednessUpdate struct { Peer peer.ID Status ConnectednessType } type PeersConnectedness map[peer.ID]ConnectednessType type GroupStatus struct { peers map[peer.ID]*PeerStatus notify *notify.Notify } type PeerStatus struct { groups map[string]*GroupStatus status ConnectednessType } type ConnectednessManager struct { peerState map[peer.ID]*PeerStatus groupState map[string]*GroupStatus muState sync.Mutex } func NewConnectednessManager() *ConnectednessManager { return &ConnectednessManager{ peerState: make(map[peer.ID]*PeerStatus), groupState: make(map[string]*GroupStatus), } } // AssociatePeer associate a peer to a group func (m *ConnectednessManager) AssociatePeer(group string, peer peer.ID) { m.muState.Lock() defer m.muState.Unlock() sg := m.getGroupStatus(group) sp := m.getPeerStatus(peer) sg.notify.L.Lock() if _, ok := sg.peers[peer]; !ok { // we got a new peer, update and signal an update sg.peers[peer] = sp sp.groups[group] = sg sg.notify.Broadcast() } sg.notify.L.Unlock() } // UpdateState update peer current connectedness state func (m *ConnectednessManager) UpdateState(peer peer.ID, update ConnectednessType) { m.muState.Lock() defer m.muState.Unlock() sp := m.getPeerStatus(peer) if sp.status != update { sp.status = update // notify each group that need an update for _, g := range sp.groups { g.notify.Broadcast() } } } // WaitForConnectednessChange wait until the given `current` peers status differ from `local` peers state func (m *ConnectednessManager) WaitForConnectednessChange(ctx context.Context, gkey string, current PeersConnectedness) ([]peer.ID, bool) { m.muState.Lock() sg := m.getGroupStatus(gkey) m.muState.Unlock() ok := true sg.notify.L.Lock() var updated []peer.ID for ok { // check if there are some diff between local state and the current state if updated = m.updateStatus(sg, current); len(updated) > 0 { break // we got some update, leave the loop } // wait until there is an update on this group or context expire // unlock notify locker ok = sg.notify.Wait(ctx) } sg.notify.L.Unlock() return updated, ok } func (m *ConnectednessManager) getGroupStatus(gkey string) *GroupStatus { s, ok := m.groupState[gkey] if !ok { s = &GroupStatus{ peers: make(map[peer.ID]*PeerStatus), notify: notify.New(&sync.Mutex{}), } m.groupState[gkey] = s } return s } func (m *ConnectednessManager) getPeerStatus(peer peer.ID) *PeerStatus { s, ok := m.peerState[peer] if !ok { s = &PeerStatus{ groups: make(map[string]*GroupStatus), } m.peerState[peer] = s } return s } func (m *ConnectednessManager) updateStatus(group *GroupStatus, current PeersConnectedness) []peer.ID { m.muState.Lock() updated := []peer.ID{} for peer := range group.peers { if ourPeer, ok := m.peerState[peer]; ok { theirStatus, ok := current[peer] if ok && ourPeer.status == theirStatus { continue // we share the same state for that peer, skip } // update peer status current[peer] = ourPeer.status updated = append(updated, peer) } } m.muState.Unlock() return updated } ================================================ FILE: consts.go ================================================ package weshnet import ( "berty.tech/go-orbit-db/cache/cacheleveldown" ) const ( NamespaceOrbitDBDatastore = "orbitdb_datastore" NamespaceOrbitDBDirectory = "orbitdb" NamespaceIPFSDatastore = "ipfs_datastore" ) var InMemoryDirectory = cacheleveldown.InMemoryDirectory ================================================ FILE: contact_request_manager.go ================================================ package weshnet import ( "bytes" "context" "encoding/base64" "encoding/hex" "fmt" "strings" "sync" "time" ipfscid "github.com/ipfs/go-cid" "github.com/libp2p/go-libp2p/core/crypto" "github.com/libp2p/go-libp2p/core/network" peer "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/p2p/host/eventbus" "go.uber.org/zap" "google.golang.org/protobuf/proto" "berty.tech/weshnet/v2/internal/handshake" "berty.tech/weshnet/v2/pkg/errcode" "berty.tech/weshnet/v2/pkg/ipfsutil" "berty.tech/weshnet/v2/pkg/logutil" "berty.tech/weshnet/v2/pkg/protocoltypes" "berty.tech/weshnet/v2/pkg/protoio" "berty.tech/weshnet/v2/pkg/tyber" ) const contactRequestV1 = "/wesh/contact_req/1.0.0" type contactRequestsManager struct { muManager sync.Mutex ctx context.Context cancel context.CancelFunc announceCancel context.CancelFunc lookupProcess map[string]context.CancelFunc muLookupProcess sync.Mutex logger *zap.Logger enabled bool ownRendezvousSeed []byte accountPrivateKey crypto.PrivKey ipfs ipfsutil.ExtendedCoreAPI swiper *Swiper metadataStore *MetadataStore } func newContactRequestsManager(s *Swiper, store *MetadataStore, ipfs ipfsutil.ExtendedCoreAPI, logger *zap.Logger) (*contactRequestsManager, error) { accountPrivateKey, err := store.secretStore.GetAccountPrivateKey() if err != nil { return nil, err } ctx, cancel := context.WithCancel(context.Background()) cm := &contactRequestsManager{ lookupProcess: make(map[string]context.CancelFunc), metadataStore: store, ipfs: ipfs, logger: logger.Named("req-mngr"), accountPrivateKey: accountPrivateKey, ctx: ctx, cancel: cancel, swiper: s, } go cm.metadataWatcher(ctx) return cm, nil } func (c *contactRequestsManager) close() { if c.isClosed() { c.logger.Warn("contactRequestsManager already closed") return } c.cancel() c.muManager.Lock() defer c.muManager.Unlock() c.enabled = false c.disableAnnounce() c.ipfs.RemoveStreamHandler(contactRequestV1) } func (c *contactRequestsManager) isClosed() bool { select { case <-c.ctx.Done(): return true default: return false } } func (c *contactRequestsManager) metadataWatcher(ctx context.Context) { handlers := map[protocoltypes.EventType]func(context.Context, *protocoltypes.GroupMetadataEvent) error{ protocoltypes.EventType_EventTypeAccountContactRequestDisabled: c.metadataRequestDisabled, protocoltypes.EventType_EventTypeAccountContactRequestEnabled: c.metadataRequestEnabled, protocoltypes.EventType_EventTypeAccountContactRequestReferenceReset: c.metadataRequestReset, protocoltypes.EventType_EventTypeAccountContactRequestOutgoingEnqueued: c.metadataRequestEnqueued, // @FIXME: looks like we don't need those events protocoltypes.EventType_EventTypeAccountContactRequestOutgoingSent: c.metadataRequestSent, protocoltypes.EventType_EventTypeAccountContactRequestIncomingReceived: c.metadataRequestReceived, } // subscribe to new event sub, err := c.metadataStore.EventBus().Subscribe(new(*protocoltypes.GroupMetadataEvent), eventbus.Name("weshnet/rqmngr/metadata-watcher")) if err != nil { c.logger.Warn("unable to subscribe to group metadata event", zap.Error(err)) return } // recreate previous contact request state enabled, contact := c.metadataStore.GetIncomingContactRequestsStatus() if contact != nil { c.ownRendezvousSeed = contact.PublicRendezvousSeed } c.muManager.Lock() if enabled { if err := c.enableContactRequest(ctx); err != nil { c.logger.Warn("unable to enable contact request", zap.Error(err)) } } c.muManager.Unlock() // enqueue all contact with the `ToRequest` state for _, contact := range c.metadataStore.ListContactsByStatus(protocoltypes.ContactState_ContactStateToRequest) { if err := c.enqueueRequest(ctx, contact); err != nil { c.logger.Warn("unable to enqueue contact request", logutil.PrivateBinary("pk", contact.Pk), zap.Error(err)) } } defer sub.Close() for { var evt any select { case evt = <-sub.Out(): case <-ctx.Done(): return } // handle new events e := evt.(*protocoltypes.GroupMetadataEvent) typ := e.GetMetadata().GetEventType() hctx, _, endSection := tyber.Section(ctx, c.logger, fmt.Sprintf("handling event - %s", typ.String())) c.muManager.Lock() var err error if handler, ok := handlers[typ]; ok { if err = handler(hctx, e); err != nil { c.logger.Error("metadata store event handler", zap.String("event", typ.String()), zap.Error(err)) } } c.muManager.Unlock() endSection(err, "") } } func (c *contactRequestsManager) metadataRequestDisabled(_ context.Context, _ *protocoltypes.GroupMetadataEvent) error { if !c.enabled { c.logger.Warn("contact request already disabled") return nil } c.enabled = false c.disableAnnounce() c.ipfs.RemoveStreamHandler(contactRequestV1) return nil } func (c *contactRequestsManager) metadataRequestEnabled(ctx context.Context, evt *protocoltypes.GroupMetadataEvent) error { e := &protocoltypes.AccountContactRequestEnabled{} if err := proto.Unmarshal(evt.Event, e); err != nil { return errcode.ErrCode_ErrDeserialization.Wrap(err) } return c.enableContactRequest(ctx) } func (c *contactRequestsManager) enableContactRequest(ctx context.Context) error { if c.enabled { c.logger.Warn("contact request already enabled") return nil } pkBytes, err := c.accountPrivateKey.GetPublic().Raw() if err != nil { return fmt.Errorf("unable to get raw pk: %w", err) } c.ipfs.SetStreamHandler(contactRequestV1, func(s network.Stream) { ctx, _, endSection := tyber.Section(c.ctx, c.logger, "receiving incoming contact request") if err := c.handleIncomingRequest(ctx, s); err != nil { c.logger.Error("unable to handle incoming contact request", zap.Error(err)) } endSection(err, "") if err := s.Reset(); err != nil { c.logger.Error("unable to reset stream", zap.Error(err)) } }) c.enabled = true tyber.LogStep(ctx, c.logger, "enabled contact request") // announce on swiper if we already got seed if c.ownRendezvousSeed != nil { return c.enableAnnounce(ctx, c.ownRendezvousSeed, pkBytes) } c.logger.Warn("no seed registered, reset will be needed before announcing") return nil } func (c *contactRequestsManager) metadataRequestReset(ctx context.Context, evt *protocoltypes.GroupMetadataEvent) error { e := &protocoltypes.AccountContactRequestReferenceReset{} if err := proto.Unmarshal(evt.Event, e); err != nil { return errcode.ErrCode_ErrDeserialization.Wrap(err) } accPK, err := c.accountPrivateKey.GetPublic().Raw() if err != nil { return fmt.Errorf("unable to get raw pk: %w", err) } switch { case e.PublicRendezvousSeed == nil: return fmt.Errorf("unable to reset with an empty seed") case bytes.Equal(e.PublicRendezvousSeed, c.ownRendezvousSeed): return fmt.Errorf("unable to reset twice with the same seed") } // updating rendezvous seed tyber.LogStep(ctx, c.logger, "update rendezvous seed") c.ownRendezvousSeed = e.PublicRendezvousSeed // if contact request manager is disable don't run announce if !c.enabled { return nil } return c.enableAnnounce(ctx, c.ownRendezvousSeed, accPK) } func (c *contactRequestsManager) metadataRequestEnqueued(ctx context.Context, evt *protocoltypes.GroupMetadataEvent) error { ctx = tyber.ContextWithConstantTraceID(ctx, "msgrcvd-"+cidBytesString(evt.EventContext.Id)) traceName := fmt.Sprintf("Received %s on group %s", strings.TrimPrefix(evt.Metadata.EventType.String(), "EventType"), base64.RawURLEncoding.EncodeToString(evt.EventContext.GroupPk)) c.logger.Debug(traceName, tyber.FormatStepLogFields(ctx, []tyber.Detail{}, tyber.UpdateTraceName(traceName))...) e := &protocoltypes.AccountContactRequestOutgoingEnqueued{} if err := proto.Unmarshal(evt.Event, e); err != nil { return tyber.LogError(ctx, c.logger, "Failed to unmarshal event", err) } // enqueue contact request if err := c.enqueueRequest(ctx, e.Contact); err != nil { return tyber.LogError(ctx, c.logger, "Failed to enqueue request", err) } return nil } func (c *contactRequestsManager) metadataRequestSent(_ context.Context, evt *protocoltypes.GroupMetadataEvent) error { e := &protocoltypes.AccountContactRequestOutgoingSent{} if err := proto.Unmarshal(evt.Event, e); err != nil { return errcode.ErrCode_ErrDeserialization.Wrap(err) } // another device may have successfully sent contact request, try to cancel // lookup if needed c.cancelContactLookup(e.ContactPk) return nil } func (c *contactRequestsManager) metadataRequestReceived(_ context.Context, evt *protocoltypes.GroupMetadataEvent) error { e := &protocoltypes.AccountContactRequestIncomingReceived{} if err := proto.Unmarshal(evt.Event, e); err != nil { return errcode.ErrCode_ErrDeserialization.Wrap(err) } // another device may have successfully sent contact request, try to cancel // lookup if needed c.cancelContactLookup(e.ContactPk) return nil } func (c *contactRequestsManager) registerContactLookup(ctx context.Context, contactPK []byte) context.Context { c.muLookupProcess.Lock() key := hex.EncodeToString(contactPK) // make sure to only have one process for this pk running ctx, cancel := context.WithCancel(ctx) if cancelProvious, ok := c.lookupProcess[key]; ok { cancelProvious() // cancel previous lookup if needed } c.lookupProcess[key] = cancel c.muLookupProcess.Unlock() return ctx } func (c *contactRequestsManager) cancelContactLookup(contactPK []byte) { c.muLookupProcess.Lock() key := hex.EncodeToString(contactPK) // cancel current lookup if needed if cancel, ok := c.lookupProcess[key]; ok { cancel() delete(c.lookupProcess, key) } c.muLookupProcess.Unlock() } func (c *contactRequestsManager) enableAnnounce(ctx context.Context, seed, accPK []byte) error { if seed == nil { return fmt.Errorf("announcing with empty seed") } if c.announceCancel != nil { // is already enable tyber.LogStep(ctx, c.logger, "canceling previous announce") c.announceCancel() } ctx, c.announceCancel = context.WithCancel(ctx) c.enabled = true tyber.LogStep(ctx, c.logger, "announcing on swipper") // start announcing on swiper, this method should take care ton announce as // many time as needed c.swiper.Announce(ctx, accPK, seed) return nil } func (c *contactRequestsManager) disableAnnounce() { if c.announceCancel != nil { c.announceCancel() c.announceCancel = nil } } func (c *contactRequestsManager) enqueueRequest(ctx context.Context, to *protocoltypes.ShareableContact) (err error) { ctx, _, endSection := tyber.Section(ctx, c.logger, "Enqueue contact request: "+base64.RawURLEncoding.EncodeToString(to.Pk)) otherPK, err := crypto.UnmarshalEd25519PublicKey(to.Pk) if err != nil { return err } if ok := c.metadataStore.checkContactStatus(otherPK, protocoltypes.ContactState_ContactStateAdded); ok { err = fmt.Errorf("contact already added") endSection(err, "") // contact already added, return err } // register lookup process ctx = c.registerContactLookup(ctx, to.Pk) // start watching topic on swiper, this method should take care of calling // `FindPeer` as many times as needed cpeers := c.swiper.WatchTopic(ctx, to.Pk, to.PublicRendezvousSeed) go func() { var err error for peer := range cpeers { // get our sharable contact to send to other contact if err = c.SendContactRequest(ctx, to, otherPK, peer); err != nil { c.logger.Warn("unable to send contact request", zap.Error(err)) } else { // successfully send contact request, leave the loop and cancel lookup break } // wait one second to avoid infinity loop on send contact request // ex: when we dont have any network, send request can fail instantly time.Sleep(time.Second) } // cancel lookup process c.cancelContactLookup(to.Pk) endSection(err, "") }() return nil } // SendContactRequest try to perform contact request with the given remote peer func (c *contactRequestsManager) SendContactRequest(ctx context.Context, to *protocoltypes.ShareableContact, otherPK crypto.PubKey, peer peer.AddrInfo) (err error) { ctx, _, endSection := tyber.Section(ctx, c.logger, "sending contact request") defer func() { endSection(err, "") }() _, own := c.metadataStore.GetIncomingContactRequestsStatus() if own == nil { err = fmt.Errorf("unable to retrieve own contact information") return err } // get own metadata for contact ownMetadata, err := c.metadataStore.GetRequestOwnMetadataForContact(to.Pk) if err != nil { c.logger.Warn("unable to get own metadata for contact", zap.Error(err)) ownMetadata = nil } own.Metadata = ownMetadata // make sure to have connection with the remote peer if err := c.ipfs.Swarm().Connect(ctx, peer); err != nil { return fmt.Errorf("unable to connect: %w", err) } // create a new stream with the remote peer stream, err := c.ipfs.NewStream(network.WithAllowLimitedConn(ctx, "req_mngr"), peer.ID, contactRequestV1) if err != nil { return fmt.Errorf("unable to open stream: %w", err) } defer func() { if err := stream.Close(); err != nil { c.logger.Warn("error while closing stream with other peer", zap.Error(err)) } }() reader := protoio.NewDelimitedReader(stream, 2048) writer := protoio.NewDelimitedWriter(stream) c.logger.Debug("performing handshake") tyber.LogStep(ctx, c.logger, "performing handshake") if err := handshake.RequestUsingReaderWriter(ctx, c.logger, reader, writer, c.accountPrivateKey, otherPK); err != nil { return fmt.Errorf("an error occurred during handshake: %w", err) } tyber.LogStep(ctx, c.logger, "sending own contact") // send own contact information if err := writer.WriteMsg(own); err != nil { return fmt.Errorf("an error occurred while sending own contact information: %w", err) } tyber.LogStep(ctx, c.logger, "mark contact request has sent") // mark this contact request as sent if _, err := c.metadataStore.ContactRequestOutgoingSent(ctx, otherPK); err != nil { return fmt.Errorf("an error occurred while marking contact request as sent: %w", err) } return nil } func (c *contactRequestsManager) handleIncomingRequest(ctx context.Context, stream network.Stream) (err error) { reader := protoio.NewDelimitedReader(stream, 2048) writer := protoio.NewDelimitedWriter(stream) tyber.LogStep(ctx, c.logger, "responding to handshake") otherPK, err := handshake.ResponseUsingReaderWriter(ctx, c.logger, reader, writer, c.accountPrivateKey) if err != nil { return fmt.Errorf("handshake failed: %w", err) } otherPKBytes, err := otherPK.Raw() if err != nil { return fmt.Errorf("failed to marshal contact public key: %w", err) } contact := &protocoltypes.ShareableContact{} tyber.LogStep(ctx, c.logger, "checking remote contact information") // read remote contact information if err := reader.ReadMsg(contact); err != nil { return fmt.Errorf("failed to read contact information: %w", err) } // validate contact pk if !bytes.Equal(otherPKBytes, contact.Pk) { return fmt.Errorf("contact information does not match handshake data") } // check contact information format if err := contact.CheckFormat(protocoltypes.ShareableContactOptionsAllowMissingRDVSeed); err != nil { return fmt.Errorf("invalid contact information format: %w", err) } tyber.LogStep(ctx, c.logger, "marking contact request has received") // mark contact request as received _, err = c.metadataStore.ContactRequestIncomingReceived(ctx, &protocoltypes.ShareableContact{ Pk: otherPKBytes, PublicRendezvousSeed: contact.PublicRendezvousSeed, Metadata: contact.Metadata, }) if err != nil { return fmt.Errorf("invalid contact information format: %w", err) } return nil } func cidBytesString(bytes []byte) string { cid, err := ipfscid.Cast(bytes) if err != nil { return "error" } return cid.String() } ================================================ FILE: contact_request_manager_test.go ================================================ package weshnet import ( "context" "io" "testing" "time" mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" "berty.tech/weshnet/v2/pkg/protocoltypes" "berty.tech/weshnet/v2/pkg/testutil" ) func TestContactRequestFlow(t *testing.T) { testutil.FilterSpeed(t, testutil.Slow) ctx, cancel := context.WithTimeout(context.Background(), time.Second*20) defer cancel() logger, cleanup := testutil.Logger(t) defer cleanup() opts := TestingOpts{ Mocknet: mocknet.New(), Logger: logger, } metadataSender1 := []byte("sender_1") pts, cleanup := NewTestingProtocolWithMockedPeers(ctx, t, &opts, nil, 2) defer cleanup() _, err := pts[0].Client.ContactRequestEnable(ctx, &protocoltypes.ContactRequestEnable_Request{}) require.NoError(t, err) _, err = pts[1].Client.ContactRequestEnable(ctx, &protocoltypes.ContactRequestEnable_Request{}) require.NoError(t, err) config0, err := pts[0].Client.ServiceGetConfiguration(ctx, &protocoltypes.ServiceGetConfiguration_Request{}) require.NoError(t, err) require.NotNil(t, config0) config1, err := pts[1].Client.ServiceGetConfiguration(ctx, &protocoltypes.ServiceGetConfiguration_Request{}) require.NoError(t, err) require.NotNil(t, config1) ref0, err := pts[0].Client.ContactRequestResetReference(ctx, &protocoltypes.ContactRequestResetReference_Request{}) require.NoError(t, err) require.NotNil(t, ref0) ref1, err := pts[1].Client.ContactRequestResetReference(ctx, &protocoltypes.ContactRequestResetReference_Request{}) require.NoError(t, err) require.NotNil(t, ref1) subCtx, subCancel := context.WithCancel(ctx) defer subCancel() subMeta0, err := pts[0].Client.GroupMetadataList(subCtx, &protocoltypes.GroupMetadataList_Request{ GroupPk: config0.AccountGroupPk, }) require.NoError(t, err) found := false _, err = pts[1].Client.ContactRequestSend(ctx, &protocoltypes.ContactRequestSend_Request{ Contact: &protocoltypes.ShareableContact{ Pk: config0.AccountPk, PublicRendezvousSeed: ref0.PublicRendezvousSeed, }, OwnMetadata: metadataSender1, }) require.NoError(t, err) for { evt, err := subMeta0.Recv() if err == io.EOF || subMeta0.Context().Err() != nil { break } require.NoError(t, err) if evt == nil || evt.Metadata.EventType != protocoltypes.EventType_EventTypeAccountContactRequestIncomingReceived { continue } req := &protocoltypes.AccountContactRequestIncomingReceived{} err = proto.Unmarshal(evt.Event, req) require.NoError(t, err) require.Equal(t, config1.AccountPk, req.ContactPk) require.Equal(t, metadataSender1, req.ContactMetadata) found = true subCancel() } require.True(t, found) _, err = pts[1].Client.ContactRequestAccept(ctx, &protocoltypes.ContactRequestAccept_Request{ ContactPk: config0.AccountPk, }) require.Error(t, err) _, err = pts[1].Client.ContactRequestAccept(ctx, &protocoltypes.ContactRequestAccept_Request{ ContactPk: config1.AccountPk, }) require.Error(t, err) _, err = pts[0].Client.ContactRequestAccept(ctx, &protocoltypes.ContactRequestAccept_Request{ ContactPk: config0.AccountPk, }) require.Error(t, err) _, err = pts[0].Client.ContactRequestAccept(ctx, &protocoltypes.ContactRequestAccept_Request{ ContactPk: config1.AccountPk, }) require.NoError(t, err) grpInfo, err := pts[0].Client.GroupInfo(ctx, &protocoltypes.GroupInfo_Request{ ContactPk: config1.AccountPk, }) require.NoError(t, err) _, err = pts[0].Client.ActivateGroup(ctx, &protocoltypes.ActivateGroup_Request{ GroupPk: grpInfo.Group.PublicKey, }) require.NoError(t, err) _, err = pts[1].Client.ActivateGroup(ctx, &protocoltypes.ActivateGroup_Request{ GroupPk: grpInfo.Group.PublicKey, }) require.NoError(t, err) } func TestContactRequestFlowWithoutIncoming(t *testing.T) { t.Skip("KUBO: this test timeout, disable it for now") testutil.FilterSpeed(t, testutil.Slow) ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() logger, cleanup := testutil.Logger(t) defer cleanup() mn := mocknet.New() defer mn.Close() opts := TestingOpts{ Mocknet: mn, Logger: logger, } metadataSender1 := []byte("sender_1") pts, cleanup := NewTestingProtocolWithMockedPeers(ctx, t, &opts, nil, 2) defer cleanup() _, err := pts[0].Client.ContactRequestEnable(ctx, &protocoltypes.ContactRequestEnable_Request{}) require.NoError(t, err) config0, err := pts[0].Client.ServiceGetConfiguration(ctx, &protocoltypes.ServiceGetConfiguration_Request{}) require.NoError(t, err) require.NotNil(t, config0) config1, err := pts[1].Client.ServiceGetConfiguration(ctx, &protocoltypes.ServiceGetConfiguration_Request{}) require.NoError(t, err) require.NotNil(t, config1) ref0, err := pts[0].Client.ContactRequestResetReference(ctx, &protocoltypes.ContactRequestResetReference_Request{}) require.NoError(t, err) require.NotNil(t, ref0) subCtx, subCancel := context.WithCancel(ctx) defer subCancel() subMeta0, err := pts[0].Client.GroupMetadataList(subCtx, &protocoltypes.GroupMetadataList_Request{ GroupPk: config0.AccountGroupPk, }) require.NoError(t, err) found := false _, err = pts[1].Client.ContactRequestSend(ctx, &protocoltypes.ContactRequestSend_Request{ Contact: &protocoltypes.ShareableContact{ Pk: config0.AccountPk, PublicRendezvousSeed: ref0.PublicRendezvousSeed, }, OwnMetadata: metadataSender1, }) require.NoError(t, err) for { evt, err := subMeta0.Recv() if err != nil { assert.NoError(t, err) break } require.NoError(t, err) if evt == nil || evt.Metadata.EventType != protocoltypes.EventType_EventTypeAccountContactRequestIncomingReceived { continue } req := &protocoltypes.AccountContactRequestIncomingReceived{} err = proto.Unmarshal(evt.Event, req) require.NoError(t, err) require.Equal(t, config1.AccountPk, req.ContactPk) require.Equal(t, metadataSender1, req.ContactMetadata) found = true subCancel() } require.True(t, found) _, err = pts[0].Client.ContactRequestAccept(ctx, &protocoltypes.ContactRequestAccept_Request{ ContactPk: config1.AccountPk, }) require.NoError(t, err) _, err = pts[0].Client.GroupInfo(ctx, &protocoltypes.GroupInfo_Request{ ContactPk: config1.AccountPk, }) require.NoError(t, err) } ================================================ FILE: deactivate_test.go ================================================ package weshnet_test import ( "context" "testing" "time" ds "github.com/ipfs/go-datastore" dsync "github.com/ipfs/go-datastore/sync" mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" "github.com/stretchr/testify/require" "berty.tech/weshnet/v2" "berty.tech/weshnet/v2/pkg/protocoltypes" "berty.tech/weshnet/v2/pkg/testutil" "berty.tech/weshnet/v2/pkg/tinder" ) func TestReactivateAccountGroup(t *testing.T) { testutil.FilterStability(t, testutil.Flappy) ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) defer cancel() logger, cleanup := testutil.Logger(t) defer cleanup() mn := mocknet.New() defer mn.Close() msrv := tinder.NewMockDriverServer() // Setup 3 nodes dsA := dsync.MutexWrap(ds.NewMapDatastore()) nodeA, closeNodeA := weshnet.NewTestingProtocol(ctx, t, &weshnet.TestingOpts{ Logger: logger.Named("nodeA"), Mocknet: mn, DiscoveryServer: msrv, }, dsA) defer closeNodeA() dsB := dsync.MutexWrap(ds.NewMapDatastore()) nodeB, closeNodeB := weshnet.NewTestingProtocol(ctx, t, &weshnet.TestingOpts{ Logger: logger.Named("nodeB"), Mocknet: mn, DiscoveryServer: msrv, }, dsB) defer closeNodeB() dsC := dsync.MutexWrap(ds.NewMapDatastore()) nodeC, closeNodeC := weshnet.NewTestingProtocol(ctx, t, &weshnet.TestingOpts{ Logger: logger.Named("nodeC"), Mocknet: mn, DiscoveryServer: msrv, }, dsC) defer closeNodeC() // make connections err := mn.LinkAll() require.NoError(t, err) err = mn.ConnectAllButSelf() require.NoError(t, err) // test communication between nodeA and nodeB nodes := []*weshnet.TestingProtocol{nodeA, nodeB} addAsContact(ctx, t, nodes, nodes) sendMessageToContact(ctx, t, []string{"pre-deactivate nodeA-nodeB"}, nodes) // reactivate nodeA account group nodeACfg, err := nodeA.Client.ServiceGetConfiguration(ctx, &protocoltypes.ServiceGetConfiguration_Request{}) require.NoError(t, err) require.NotNil(t, nodeACfg) _, err = nodeA.Client.DeactivateGroup(ctx, &protocoltypes.DeactivateGroup_Request{ GroupPk: nodeACfg.AccountGroupPk, }) require.NoError(t, err) _, err = nodeA.Client.ActivateGroup(ctx, &protocoltypes.ActivateGroup_Request{ GroupPk: nodeACfg.AccountGroupPk, }) require.NoError(t, err) // test communication between nodeA and nodeC nodes = []*weshnet.TestingProtocol{nodeA, nodeC} addAsContact(ctx, t, nodes, nodes) sendMessageToContact(ctx, t, []string{"post reactivate nodeA-nodeC"}, nodes) } func TestRaceReactivateAccountGroup(t *testing.T) { testutil.FilterStability(t, testutil.Flappy) ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) defer cancel() logger, cleanup := testutil.Logger(t) defer cleanup() mn := mocknet.New() defer mn.Close() msrv := tinder.NewMockDriverServer() // Setup 2 nodes dsA := dsync.MutexWrap(ds.NewMapDatastore()) nodeA, closeNodeA := weshnet.NewTestingProtocol(ctx, t, &weshnet.TestingOpts{ Logger: logger.Named("nodeA"), Mocknet: mn, DiscoveryServer: msrv, }, dsA) defer closeNodeA() dsB := dsync.MutexWrap(ds.NewMapDatastore()) nodeB, closeNodeB := weshnet.NewTestingProtocol(ctx, t, &weshnet.TestingOpts{ Logger: logger.Named("nodeB"), Mocknet: mn, DiscoveryServer: msrv, }, dsB) defer closeNodeB() // make connections err := mn.LinkAll() require.NoError(t, err) err = mn.ConnectAllButSelf() require.NoError(t, err) // reactivate nodeA account group nodeACfg, err := nodeA.Client.ServiceGetConfiguration(ctx, &protocoltypes.ServiceGetConfiguration_Request{}) require.NoError(t, err) require.NotNil(t, nodeACfg) deactivateFunc := func() { t.Log("DeactivateGroup") _, err := nodeA.Client.DeactivateGroup(ctx, &protocoltypes.DeactivateGroup_Request{ GroupPk: nodeACfg.AccountGroupPk, }) require.NoError(t, err) } activateFunc := func() { t.Log("ActivateGroup") _, err := nodeA.Client.ActivateGroup(ctx, &protocoltypes.ActivateGroup_Request{ GroupPk: nodeACfg.AccountGroupPk, }) require.NoError(t, err) } go deactivateFunc() time.Sleep(1 * time.Millisecond) go activateFunc() // test communication between nodeA and nodeB time.Sleep(3 * time.Second) nodes := []*weshnet.TestingProtocol{nodeA, nodeB} t.Log("addAsContact") addAsContact(ctx, t, nodes, nodes) t.Log("sendMessageToContact") sendMessageToContact(ctx, t, []string{"nodeA-nodeB"}, nodes) } func TestReactivateContactGroup(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) defer cancel() logger, cleanup := testutil.Logger(t) defer cleanup() opts := weshnet.TestingOpts{ Mocknet: mocknet.New(), Logger: logger, ConnectFunc: weshnet.ConnectAll, } nodes, cleanup := weshnet.NewTestingProtocolWithMockedPeers(ctx, t, &opts, nil, 2) defer cleanup() addAsContact(ctx, t, nodes, nodes) // send messages before deactivating sendMessageToContact(ctx, t, []string{"pre-deactivate"}, nodes) // get contact group contactGroup := getContactGroup(ctx, t, nodes[0], nodes[1]) // deactivate contact group _, err := nodes[0].Client.DeactivateGroup(ctx, &protocoltypes.DeactivateGroup_Request{ GroupPk: contactGroup.Group.PublicKey, }) require.NoError(t, err) // reactivate group _, err = nodes[0].Client.ActivateGroup(ctx, &protocoltypes.ActivateGroup_Request{ GroupPk: contactGroup.Group.PublicKey, }) require.NoError(t, err) // send message after reactivating sendMessageToContact(ctx, t, []string{"post-reactivate"}, nodes) } func TestRaceReactivateContactGroup(t *testing.T) { testutil.FilterStability(t, testutil.Flappy) ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) defer cancel() logger, cleanup := testutil.Logger(t) defer cleanup() opts := weshnet.TestingOpts{ Mocknet: mocknet.New(), Logger: logger, ConnectFunc: weshnet.ConnectAll, } nodes, cleanup := weshnet.NewTestingProtocolWithMockedPeers(ctx, t, &opts, nil, 2) defer cleanup() t.Log("addAsContact") addAsContact(ctx, t, nodes, nodes) // send messages before deactivating t.Log("sendMessageToContact") sendMessageToContact(ctx, t, []string{"pre-deactivate"}, nodes) // get contact group contactGroup := getContactGroup(ctx, t, nodes[0], nodes[1]) // deactivate contact group deactivateFunc := func() { t.Log("DeactivateGroup") _, err := nodes[0].Client.DeactivateGroup(ctx, &protocoltypes.DeactivateGroup_Request{ GroupPk: contactGroup.Group.PublicKey, }) require.NoError(t, err) } // reactivate group activateFunc := func() { t.Log("ActivateGroup") _, err := nodes[0].Client.ActivateGroup(ctx, &protocoltypes.ActivateGroup_Request{ GroupPk: contactGroup.Group.PublicKey, }) require.NoError(t, err) } go deactivateFunc() time.Sleep(1 * time.Millisecond) go activateFunc() // send message after reactivating time.Sleep(5 * time.Second) t.Log("sendMessageToContact") sendMessageToContact(ctx, t, []string{"post-reactivate"}, nodes) } func TestReactivateMultimemberGroup(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) defer cancel() logger, cleanup := testutil.Logger(t) defer cleanup() opts := weshnet.TestingOpts{ Mocknet: mocknet.New(), Logger: logger, ConnectFunc: weshnet.ConnectAll, } nodes, cleanup := weshnet.NewTestingProtocolWithMockedPeers(ctx, t, &opts, nil, 2) defer cleanup() // Create MultiMember Group group := weshnet.CreateMultiMemberGroupInstance(ctx, t, nodes[0], nodes[1]) // Send message before deactivation sendMessageOnGroup(ctx, t, nodes, nodes, group.PublicKey, []string{"pre-deactivate"}) // deactivate multimember group _, err := nodes[0].Client.DeactivateGroup(ctx, &protocoltypes.DeactivateGroup_Request{ GroupPk: group.PublicKey, }) require.NoError(t, err) // reactivate group _, err = nodes[0].Client.ActivateGroup(ctx, &protocoltypes.ActivateGroup_Request{ GroupPk: group.PublicKey, }) require.NoError(t, err) // Send message after reactivation sendMessageOnGroup(ctx, t, nodes, nodes, group.PublicKey, []string{"post-deactivate"}) } ================================================ FILE: doc.go ================================================ // Package weshnet contains code for integrating the Berty protocol in your project. // // See https://berty.tech/protocol for more information. package weshnet ================================================ FILE: docs/CONTRIBUTING.md ================================================ # Contributing Please visit https://github.com/berty/community/blob/master/CONTRIBUTING.md ================================================ FILE: docs/Makefile ================================================ ## ## Code gen ## VERSION ?= `go run github.com/mdomke/git-semver/v5` all: generate gen_src := $(wildcard ../api/*.proto) $(wildcard ../api/*.yaml) Makefile gen_sum := gen.sum generate: gen.sum $(gen_sum): $(gen_src) @shasum $(gen_src) | sort -k 2 > $(gen_sum).tmp @diff -q $(gen_sum).tmp $(gen_sum) || ( \ set -xe; \ (set -e; GO111MODULE=on go mod download); \ docker run \ --user=`id -u` \ --volume="$(PWD)/..:/go/src/berty.tech/berty" \ --volume="`go env GOPATH`/pkg/mod:/go/pkg/mod" \ --workdir="/go/src/berty.tech/berty/docs" \ --entrypoint="sh" \ --rm \ bertytech/buf:5 \ -xec 'make generate_local' \ ) .PHONY: generate protoc_opts := -I ../api:`go list -m -mod=mod -f {{.Dir}} github.com/grpc-ecosystem/grpc-gateway`/third_party/googleapis generate_local: mkdir -p protocol buf generate --template ./buf-doc.gen.yaml ../api/protocol/protocoltypes.proto -o protocol @# replace multiple empty lines with one cat protocol/api.md.tmp | sed '/^$$/N;/^\n$$/D' > protocol/api.md rm -f */*.md.tmp shasum $(gen_src) | sort -k 2 > $(gen_sum).tmp mv $(gen_sum).tmp $(gen_sum) mv protocol/api.md apis/protocoltypes.md mv protocol/protocoltypes.swagger.json apis/ .PHONY: generate_local regenerate: gen.clean generate .PHONY: regenerate gen.clean: rm -f gen.sum $(wildcard */*.md.tmp) $(wildcard */*.swagger.json) .PHONY: gen.clean openapi.prepare: gen.sum mkdir -p .tmp/openapi cat ./apis/protocoltypes.swagger.json | jq '.info.version="'$(VERSION)'"' > .tmp/openapi/protocoltypes.swagger.json cat .tmp/openapi/*.json | jq .info.version .PHONY: openapi.prepare BUMP_TOKEN ?= bump.validate: openapi.prepare @# gem install bump-cli bump validate --token=$(BUMP_TOKEN) --doc=6eb1bb1e-c65d-4b73-a8c4-0e545742f6db .tmp/openapi/protocoltypes.swagger.json .PHONY: bump.validate bump.deploy: bump.validate @# gem install bump-cli bump deploy --token=$(BUMP_TOKEN) --doc=6eb1bb1e-c65d-4b73-a8c4-0e545742f6db .tmp/openapi/protocoltypes.swagger.json .PHONY: bump.deploy openapi.clean: rm -rf .tmp/openapi .PHONY: openapi.clean clean: gen.clean openapi.clean .PHONY: clean tidy: .PHONY: tidy ================================================ FILE: docs/apis/protocoltypes.md ================================================ # Protocol Documentation ## Table of Contents - [protocoltypes.proto](#protocoltypes-proto) - [Account](#weshnet-protocol-v1-Account) - [AccountContactBlocked](#weshnet-protocol-v1-AccountContactBlocked) - [AccountContactRequestDisabled](#weshnet-protocol-v1-AccountContactRequestDisabled) - [AccountContactRequestEnabled](#weshnet-protocol-v1-AccountContactRequestEnabled) - [AccountContactRequestIncomingAccepted](#weshnet-protocol-v1-AccountContactRequestIncomingAccepted) - [AccountContactRequestIncomingDiscarded](#weshnet-protocol-v1-AccountContactRequestIncomingDiscarded) - [AccountContactRequestIncomingReceived](#weshnet-protocol-v1-AccountContactRequestIncomingReceived) - [AccountContactRequestOutgoingEnqueued](#weshnet-protocol-v1-AccountContactRequestOutgoingEnqueued) - [AccountContactRequestOutgoingSent](#weshnet-protocol-v1-AccountContactRequestOutgoingSent) - [AccountContactRequestReferenceReset](#weshnet-protocol-v1-AccountContactRequestReferenceReset) - [AccountContactUnblocked](#weshnet-protocol-v1-AccountContactUnblocked) - [AccountGroupJoined](#weshnet-protocol-v1-AccountGroupJoined) - [AccountGroupLeft](#weshnet-protocol-v1-AccountGroupLeft) - [AccountVerifiedCredentialRegistered](#weshnet-protocol-v1-AccountVerifiedCredentialRegistered) - [ActivateGroup](#weshnet-protocol-v1-ActivateGroup) - [ActivateGroup.Reply](#weshnet-protocol-v1-ActivateGroup-Reply) - [ActivateGroup.Request](#weshnet-protocol-v1-ActivateGroup-Request) - [AppMessageSend](#weshnet-protocol-v1-AppMessageSend) - [AppMessageSend.Reply](#weshnet-protocol-v1-AppMessageSend-Reply) - [AppMessageSend.Request](#weshnet-protocol-v1-AppMessageSend-Request) - [AppMetadataSend](#weshnet-protocol-v1-AppMetadataSend) - [AppMetadataSend.Reply](#weshnet-protocol-v1-AppMetadataSend-Reply) - [AppMetadataSend.Request](#weshnet-protocol-v1-AppMetadataSend-Request) - [ContactAliasKeyAdded](#weshnet-protocol-v1-ContactAliasKeyAdded) - [ContactAliasKeySend](#weshnet-protocol-v1-ContactAliasKeySend) - [ContactAliasKeySend.Reply](#weshnet-protocol-v1-ContactAliasKeySend-Reply) - [ContactAliasKeySend.Request](#weshnet-protocol-v1-ContactAliasKeySend-Request) - [ContactBlock](#weshnet-protocol-v1-ContactBlock) - [ContactBlock.Reply](#weshnet-protocol-v1-ContactBlock-Reply) - [ContactBlock.Request](#weshnet-protocol-v1-ContactBlock-Request) - [ContactRequestAccept](#weshnet-protocol-v1-ContactRequestAccept) - [ContactRequestAccept.Reply](#weshnet-protocol-v1-ContactRequestAccept-Reply) - [ContactRequestAccept.Request](#weshnet-protocol-v1-ContactRequestAccept-Request) - [ContactRequestDisable](#weshnet-protocol-v1-ContactRequestDisable) - [ContactRequestDisable.Reply](#weshnet-protocol-v1-ContactRequestDisable-Reply) - [ContactRequestDisable.Request](#weshnet-protocol-v1-ContactRequestDisable-Request) - [ContactRequestDiscard](#weshnet-protocol-v1-ContactRequestDiscard) - [ContactRequestDiscard.Reply](#weshnet-protocol-v1-ContactRequestDiscard-Reply) - [ContactRequestDiscard.Request](#weshnet-protocol-v1-ContactRequestDiscard-Request) - [ContactRequestEnable](#weshnet-protocol-v1-ContactRequestEnable) - [ContactRequestEnable.Reply](#weshnet-protocol-v1-ContactRequestEnable-Reply) - [ContactRequestEnable.Request](#weshnet-protocol-v1-ContactRequestEnable-Request) - [ContactRequestReference](#weshnet-protocol-v1-ContactRequestReference) - [ContactRequestReference.Reply](#weshnet-protocol-v1-ContactRequestReference-Reply) - [ContactRequestReference.Request](#weshnet-protocol-v1-ContactRequestReference-Request) - [ContactRequestResetReference](#weshnet-protocol-v1-ContactRequestResetReference) - [ContactRequestResetReference.Reply](#weshnet-protocol-v1-ContactRequestResetReference-Reply) - [ContactRequestResetReference.Request](#weshnet-protocol-v1-ContactRequestResetReference-Request) - [ContactRequestSend](#weshnet-protocol-v1-ContactRequestSend) - [ContactRequestSend.Reply](#weshnet-protocol-v1-ContactRequestSend-Reply) - [ContactRequestSend.Request](#weshnet-protocol-v1-ContactRequestSend-Request) - [ContactUnblock](#weshnet-protocol-v1-ContactUnblock) - [ContactUnblock.Reply](#weshnet-protocol-v1-ContactUnblock-Reply) - [ContactUnblock.Request](#weshnet-protocol-v1-ContactUnblock-Request) - [CredentialVerificationServiceCompleteFlow](#weshnet-protocol-v1-CredentialVerificationServiceCompleteFlow) - [CredentialVerificationServiceCompleteFlow.Reply](#weshnet-protocol-v1-CredentialVerificationServiceCompleteFlow-Reply) - [CredentialVerificationServiceCompleteFlow.Request](#weshnet-protocol-v1-CredentialVerificationServiceCompleteFlow-Request) - [CredentialVerificationServiceInitFlow](#weshnet-protocol-v1-CredentialVerificationServiceInitFlow) - [CredentialVerificationServiceInitFlow.Reply](#weshnet-protocol-v1-CredentialVerificationServiceInitFlow-Reply) - [CredentialVerificationServiceInitFlow.Request](#weshnet-protocol-v1-CredentialVerificationServiceInitFlow-Request) - [DeactivateGroup](#weshnet-protocol-v1-DeactivateGroup) - [DeactivateGroup.Reply](#weshnet-protocol-v1-DeactivateGroup-Reply) - [DeactivateGroup.Request](#weshnet-protocol-v1-DeactivateGroup-Request) - [DebugGroup](#weshnet-protocol-v1-DebugGroup) - [DebugGroup.Reply](#weshnet-protocol-v1-DebugGroup-Reply) - [DebugGroup.Request](#weshnet-protocol-v1-DebugGroup-Request) - [DebugInspectGroupStore](#weshnet-protocol-v1-DebugInspectGroupStore) - [DebugInspectGroupStore.Reply](#weshnet-protocol-v1-DebugInspectGroupStore-Reply) - [DebugInspectGroupStore.Request](#weshnet-protocol-v1-DebugInspectGroupStore-Request) - [DebugListGroups](#weshnet-protocol-v1-DebugListGroups) - [DebugListGroups.Reply](#weshnet-protocol-v1-DebugListGroups-Reply) - [DebugListGroups.Request](#weshnet-protocol-v1-DebugListGroups-Request) - [DecodeContact](#weshnet-protocol-v1-DecodeContact) - [DecodeContact.Reply](#weshnet-protocol-v1-DecodeContact-Reply) - [DecodeContact.Request](#weshnet-protocol-v1-DecodeContact-Request) - [DeviceChainKey](#weshnet-protocol-v1-DeviceChainKey) - [EncryptedMessage](#weshnet-protocol-v1-EncryptedMessage) - [EventContext](#weshnet-protocol-v1-EventContext) - [FirstLastCounters](#weshnet-protocol-v1-FirstLastCounters) - [Group](#weshnet-protocol-v1-Group) - [GroupAddAdditionalRendezvousSeed](#weshnet-protocol-v1-GroupAddAdditionalRendezvousSeed) - [GroupDeviceChainKeyAdded](#weshnet-protocol-v1-GroupDeviceChainKeyAdded) - [GroupDeviceStatus](#weshnet-protocol-v1-GroupDeviceStatus) - [GroupDeviceStatus.Reply](#weshnet-protocol-v1-GroupDeviceStatus-Reply) - [GroupDeviceStatus.Reply.PeerConnected](#weshnet-protocol-v1-GroupDeviceStatus-Reply-PeerConnected) - [GroupDeviceStatus.Reply.PeerDisconnected](#weshnet-protocol-v1-GroupDeviceStatus-Reply-PeerDisconnected) - [GroupDeviceStatus.Reply.PeerReconnecting](#weshnet-protocol-v1-GroupDeviceStatus-Reply-PeerReconnecting) - [GroupDeviceStatus.Request](#weshnet-protocol-v1-GroupDeviceStatus-Request) - [GroupEnvelope](#weshnet-protocol-v1-GroupEnvelope) - [GroupHeadsExport](#weshnet-protocol-v1-GroupHeadsExport) - [GroupInfo](#weshnet-protocol-v1-GroupInfo) - [GroupInfo.Reply](#weshnet-protocol-v1-GroupInfo-Reply) - [GroupInfo.Request](#weshnet-protocol-v1-GroupInfo-Request) - [GroupMemberDeviceAdded](#weshnet-protocol-v1-GroupMemberDeviceAdded) - [GroupMessageEvent](#weshnet-protocol-v1-GroupMessageEvent) - [GroupMessageList](#weshnet-protocol-v1-GroupMessageList) - [GroupMessageList.Request](#weshnet-protocol-v1-GroupMessageList-Request) - [GroupMetadata](#weshnet-protocol-v1-GroupMetadata) - [GroupMetadataEvent](#weshnet-protocol-v1-GroupMetadataEvent) - [GroupMetadataList](#weshnet-protocol-v1-GroupMetadataList) - [GroupMetadataList.Request](#weshnet-protocol-v1-GroupMetadataList-Request) - [GroupMetadataPayloadSent](#weshnet-protocol-v1-GroupMetadataPayloadSent) - [GroupRemoveAdditionalRendezvousSeed](#weshnet-protocol-v1-GroupRemoveAdditionalRendezvousSeed) - [GroupReplicating](#weshnet-protocol-v1-GroupReplicating) - [MessageEnvelope](#weshnet-protocol-v1-MessageEnvelope) - [MessageHeaders](#weshnet-protocol-v1-MessageHeaders) - [MessageHeaders.MetadataEntry](#weshnet-protocol-v1-MessageHeaders-MetadataEntry) - [MultiMemberGroupAdminRoleGrant](#weshnet-protocol-v1-MultiMemberGroupAdminRoleGrant) - [MultiMemberGroupAdminRoleGrant.Reply](#weshnet-protocol-v1-MultiMemberGroupAdminRoleGrant-Reply) - [MultiMemberGroupAdminRoleGrant.Request](#weshnet-protocol-v1-MultiMemberGroupAdminRoleGrant-Request) - [MultiMemberGroupAdminRoleGranted](#weshnet-protocol-v1-MultiMemberGroupAdminRoleGranted) - [MultiMemberGroupAliasResolverAdded](#weshnet-protocol-v1-MultiMemberGroupAliasResolverAdded) - [MultiMemberGroupAliasResolverDisclose](#weshnet-protocol-v1-MultiMemberGroupAliasResolverDisclose) - [MultiMemberGroupAliasResolverDisclose.Reply](#weshnet-protocol-v1-MultiMemberGroupAliasResolverDisclose-Reply) - [MultiMemberGroupAliasResolverDisclose.Request](#weshnet-protocol-v1-MultiMemberGroupAliasResolverDisclose-Request) - [MultiMemberGroupCreate](#weshnet-protocol-v1-MultiMemberGroupCreate) - [MultiMemberGroupCreate.Reply](#weshnet-protocol-v1-MultiMemberGroupCreate-Reply) - [MultiMemberGroupCreate.Request](#weshnet-protocol-v1-MultiMemberGroupCreate-Request) - [MultiMemberGroupInitialMemberAnnounced](#weshnet-protocol-v1-MultiMemberGroupInitialMemberAnnounced) - [MultiMemberGroupInvitationCreate](#weshnet-protocol-v1-MultiMemberGroupInvitationCreate) - [MultiMemberGroupInvitationCreate.Reply](#weshnet-protocol-v1-MultiMemberGroupInvitationCreate-Reply) - [MultiMemberGroupInvitationCreate.Request](#weshnet-protocol-v1-MultiMemberGroupInvitationCreate-Request) - [MultiMemberGroupJoin](#weshnet-protocol-v1-MultiMemberGroupJoin) - [MultiMemberGroupJoin.Reply](#weshnet-protocol-v1-MultiMemberGroupJoin-Reply) - [MultiMemberGroupJoin.Request](#weshnet-protocol-v1-MultiMemberGroupJoin-Request) - [MultiMemberGroupLeave](#weshnet-protocol-v1-MultiMemberGroupLeave) - [MultiMemberGroupLeave.Reply](#weshnet-protocol-v1-MultiMemberGroupLeave-Reply) - [MultiMemberGroupLeave.Request](#weshnet-protocol-v1-MultiMemberGroupLeave-Request) - [OrbitDBMessageHeads](#weshnet-protocol-v1-OrbitDBMessageHeads) - [OrbitDBMessageHeads.Box](#weshnet-protocol-v1-OrbitDBMessageHeads-Box) - [OutOfStoreMessage](#weshnet-protocol-v1-OutOfStoreMessage) - [OutOfStoreMessageEnvelope](#weshnet-protocol-v1-OutOfStoreMessageEnvelope) - [OutOfStoreReceive](#weshnet-protocol-v1-OutOfStoreReceive) - [OutOfStoreReceive.Reply](#weshnet-protocol-v1-OutOfStoreReceive-Reply) - [OutOfStoreReceive.Request](#weshnet-protocol-v1-OutOfStoreReceive-Request) - [OutOfStoreSeal](#weshnet-protocol-v1-OutOfStoreSeal) - [OutOfStoreSeal.Reply](#weshnet-protocol-v1-OutOfStoreSeal-Reply) - [OutOfStoreSeal.Request](#weshnet-protocol-v1-OutOfStoreSeal-Request) - [PeerList](#weshnet-protocol-v1-PeerList) - [PeerList.Peer](#weshnet-protocol-v1-PeerList-Peer) - [PeerList.Reply](#weshnet-protocol-v1-PeerList-Reply) - [PeerList.Request](#weshnet-protocol-v1-PeerList-Request) - [PeerList.Route](#weshnet-protocol-v1-PeerList-Route) - [PeerList.Stream](#weshnet-protocol-v1-PeerList-Stream) - [Progress](#weshnet-protocol-v1-Progress) - [ProtocolMetadata](#weshnet-protocol-v1-ProtocolMetadata) - [RefreshContactRequest](#weshnet-protocol-v1-RefreshContactRequest) - [RefreshContactRequest.Peer](#weshnet-protocol-v1-RefreshContactRequest-Peer) - [RefreshContactRequest.Reply](#weshnet-protocol-v1-RefreshContactRequest-Reply) - [RefreshContactRequest.Request](#weshnet-protocol-v1-RefreshContactRequest-Request) - [ReplicationServiceRegisterGroup](#weshnet-protocol-v1-ReplicationServiceRegisterGroup) - [ReplicationServiceRegisterGroup.Reply](#weshnet-protocol-v1-ReplicationServiceRegisterGroup-Reply) - [ReplicationServiceRegisterGroup.Request](#weshnet-protocol-v1-ReplicationServiceRegisterGroup-Request) - [ReplicationServiceReplicateGroup](#weshnet-protocol-v1-ReplicationServiceReplicateGroup) - [ReplicationServiceReplicateGroup.Reply](#weshnet-protocol-v1-ReplicationServiceReplicateGroup-Reply) - [ReplicationServiceReplicateGroup.Request](#weshnet-protocol-v1-ReplicationServiceReplicateGroup-Request) - [ServiceExportData](#weshnet-protocol-v1-ServiceExportData) - [ServiceExportData.Reply](#weshnet-protocol-v1-ServiceExportData-Reply) - [ServiceExportData.Request](#weshnet-protocol-v1-ServiceExportData-Request) - [ServiceGetConfiguration](#weshnet-protocol-v1-ServiceGetConfiguration) - [ServiceGetConfiguration.Reply](#weshnet-protocol-v1-ServiceGetConfiguration-Reply) - [ServiceGetConfiguration.Request](#weshnet-protocol-v1-ServiceGetConfiguration-Request) - [ServiceToken](#weshnet-protocol-v1-ServiceToken) - [ServiceTokenSupportedService](#weshnet-protocol-v1-ServiceTokenSupportedService) - [ShareContact](#weshnet-protocol-v1-ShareContact) - [ShareContact.Reply](#weshnet-protocol-v1-ShareContact-Reply) - [ShareContact.Request](#weshnet-protocol-v1-ShareContact-Request) - [ShareableContact](#weshnet-protocol-v1-ShareableContact) - [SystemInfo](#weshnet-protocol-v1-SystemInfo) - [SystemInfo.OrbitDB](#weshnet-protocol-v1-SystemInfo-OrbitDB) - [SystemInfo.OrbitDB.ReplicationStatus](#weshnet-protocol-v1-SystemInfo-OrbitDB-ReplicationStatus) - [SystemInfo.P2P](#weshnet-protocol-v1-SystemInfo-P2P) - [SystemInfo.Process](#weshnet-protocol-v1-SystemInfo-Process) - [SystemInfo.Reply](#weshnet-protocol-v1-SystemInfo-Reply) - [SystemInfo.Request](#weshnet-protocol-v1-SystemInfo-Request) - [VerifiedCredentialsList](#weshnet-protocol-v1-VerifiedCredentialsList) - [VerifiedCredentialsList.Reply](#weshnet-protocol-v1-VerifiedCredentialsList-Reply) - [VerifiedCredentialsList.Request](#weshnet-protocol-v1-VerifiedCredentialsList-Request) - [ContactState](#weshnet-protocol-v1-ContactState) - [DebugInspectGroupLogType](#weshnet-protocol-v1-DebugInspectGroupLogType) - [Direction](#weshnet-protocol-v1-Direction) - [EventType](#weshnet-protocol-v1-EventType) - [GroupDeviceStatus.Transport](#weshnet-protocol-v1-GroupDeviceStatus-Transport) - [GroupDeviceStatus.Type](#weshnet-protocol-v1-GroupDeviceStatus-Type) - [GroupType](#weshnet-protocol-v1-GroupType) - [PeerList.Feature](#weshnet-protocol-v1-PeerList-Feature) - [ServiceGetConfiguration.SettingState](#weshnet-protocol-v1-ServiceGetConfiguration-SettingState) - [ProtocolService](#weshnet-protocol-v1-ProtocolService) - [Scalar Value Types](#scalar-value-types)

Top

## protocoltypes.proto ### Account Account describes all the secrets that identifies an Account | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | group | [Group](#weshnet-protocol-v1-Group) | | group specifies which group is used to manage the account | | 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 | | 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 | | public_rendezvous_seed | [bytes](#bytes) | | public_rendezvous_seed, rendezvous seed used for direct communication | ### AccountContactBlocked AccountContactBlocked indicates that a contact is blocked | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | device_pk | [bytes](#bytes) | | device_pk is the device sending the event, signs the message | | contact_pk | [bytes](#bytes) | | contact_pk is the contact blocked | ### AccountContactRequestDisabled AccountContactRequestDisabled indicates that the account should not be advertised on a public rendezvous point | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | device_pk | [bytes](#bytes) | | device_pk is the device sending the event, signs the message | ### AccountContactRequestEnabled AccountContactRequestEnabled indicates that the account should be advertised on a public rendezvous point | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | device_pk | [bytes](#bytes) | | device_pk is the device sending the event, signs the message | ### AccountContactRequestIncomingAccepted This event should be followed by an AccountGroupJoined event This event should be followed by GroupMemberDeviceAdded and GroupDeviceChainKeyAdded events within the AccountGroup AccountContactRequestIncomingAccepted indicates that a contact request has been accepted | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | device_pk | [bytes](#bytes) | | device_pk is the device sending the event, signs the message | | contact_pk | [bytes](#bytes) | | contact_pk is the contact whom request is accepted | | group_pk | [bytes](#bytes) | | group_pk is the 1to1 group with the requester user | ### AccountContactRequestIncomingDiscarded AccountContactRequestIncomingDiscarded indicates that a contact request has been refused | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | device_pk | [bytes](#bytes) | | device_pk is the device sending the event, signs the message | | contact_pk | [bytes](#bytes) | | contact_pk is the contact whom request is refused | ### AccountContactRequestIncomingReceived AccountContactRequestIncomingReceived indicates that the account has received a new contact request | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | device_pk | [bytes](#bytes) | | device_pk is the device sending the account event (which received the contact request), signs the message | | contact_pk | [bytes](#bytes) | | contact_pk is the account sending the request | | contact_rendezvous_seed | [bytes](#bytes) | | TODO: is this necessary? contact_rendezvous_seed is the rendezvous seed of the contact sending the request | | contact_metadata | [bytes](#bytes) | | TODO: is this necessary? contact_metadata is the metadata specific to the app to identify the contact for the request | ### AccountContactRequestOutgoingEnqueued This event should be followed by an AccountGroupJoined event This event should be followed by a GroupMemberDeviceAdded event within the AccountGroup This event should be followed by a GroupDeviceChainKeyAdded event within the AccountGroup AccountContactRequestOutgoingEnqueued indicates that the account will attempt to send a contact request when a matching peer is discovered | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | device_pk | [bytes](#bytes) | | device_pk is the device sending the event, signs the message | | group_pk | [bytes](#bytes) | | group_pk is the 1to1 group with the requested user | | contact | [ShareableContact](#weshnet-protocol-v1-ShareableContact) | | contact is a message describing how to connect to the other account | | own_metadata | [bytes](#bytes) | | own_metadata is the identifying metadata that will be shared to the other account | ### AccountContactRequestOutgoingSent AccountContactRequestOutgoingSent indicates that the account has sent a contact request | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | device_pk | [bytes](#bytes) | | device_pk is the device sending the account event, signs the message | | contact_pk | [bytes](#bytes) | | contact_pk is the contacted account | ### AccountContactRequestReferenceReset AccountContactRequestReferenceReset indicates that the account should be advertised on different public rendezvous points | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | device_pk | [bytes](#bytes) | | device_pk is the device sending the event, signs the message | | public_rendezvous_seed | [bytes](#bytes) | | public_rendezvous_seed is the new rendezvous point seed | ### AccountContactUnblocked AccountContactUnblocked indicates that a contact is unblocked | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | device_pk | [bytes](#bytes) | | device_pk is the device sending the event, signs the message | | contact_pk | [bytes](#bytes) | | contact_pk is the contact unblocked | ### AccountGroupJoined AccountGroupJoined indicates that the account is now part of a new group | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | device_pk | [bytes](#bytes) | | device_pk is the device sending the event, signs the message | | group | [Group](#weshnet-protocol-v1-Group) | | group describe the joined group | ### AccountGroupLeft AccountGroupLeft indicates that the account has left a group | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | device_pk | [bytes](#bytes) | | device_pk is the device sending the event, signs the message | | group_pk | [bytes](#bytes) | | group_pk references the group left | ### AccountVerifiedCredentialRegistered | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | device_pk | [bytes](#bytes) | | device_pk is the public key of the device sending the message | | signed_identity_public_key | [bytes](#bytes) | | | | verified_credential | [string](#string) | | | | registration_date | [int64](#int64) | | | | expiration_date | [int64](#int64) | | | | identifier | [string](#string) | | | | issuer | [string](#string) | | | ### ActivateGroup ### ActivateGroup.Reply ### ActivateGroup.Request | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | group_pk | [bytes](#bytes) | | group_pk is the identifier of the group | | local_only | [bool](#bool) | | local_only will open the group without enabling network interactions with other members | ### AppMessageSend ### AppMessageSend.Reply | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | cid | [bytes](#bytes) | | | ### AppMessageSend.Request | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | group_pk | [bytes](#bytes) | | group_pk is the identifier of the group | | payload | [bytes](#bytes) | | payload is the payload to send | ### AppMetadataSend ### AppMetadataSend.Reply | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | cid | [bytes](#bytes) | | | ### AppMetadataSend.Request | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | group_pk | [bytes](#bytes) | | group_pk is the identifier of the group | | payload | [bytes](#bytes) | | payload is the payload to send | ### ContactAliasKeyAdded ContactAliasKeyAdded is an event type where ones shares their alias public key | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | device_pk | [bytes](#bytes) | | device_pk is the device sending the event, signs the message | | alias_pk | [bytes](#bytes) | | alias_pk is the alias key which will be used to verify a contact identity | ### ContactAliasKeySend ### ContactAliasKeySend.Reply ### ContactAliasKeySend.Request | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | group_pk | [bytes](#bytes) | | contact_pk is the identifier of the contact to send the alias public key to | ### ContactBlock ### ContactBlock.Reply ### ContactBlock.Request | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | contact_pk | [bytes](#bytes) | | contact_pk is the identifier of the contact to block | ### ContactRequestAccept ### ContactRequestAccept.Reply ### ContactRequestAccept.Request | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | contact_pk | [bytes](#bytes) | | contact_pk is the identifier of the contact to accept the request from | ### ContactRequestDisable ### ContactRequestDisable.Reply ### ContactRequestDisable.Request ### ContactRequestDiscard ### ContactRequestDiscard.Reply ### ContactRequestDiscard.Request | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | contact_pk | [bytes](#bytes) | | contact_pk is the identifier of the contact to ignore the request from | ### ContactRequestEnable ### ContactRequestEnable.Reply | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | public_rendezvous_seed | [bytes](#bytes) | | public_rendezvous_seed is the rendezvous seed used by the current account | ### ContactRequestEnable.Request ### ContactRequestReference ### ContactRequestReference.Reply | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | public_rendezvous_seed | [bytes](#bytes) | | public_rendezvous_seed is the rendezvous seed used by the current account | | enabled | [bool](#bool) | | enabled indicates if incoming contact requests are enabled | ### ContactRequestReference.Request ### ContactRequestResetReference ### ContactRequestResetReference.Reply | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | public_rendezvous_seed | [bytes](#bytes) | | public_rendezvous_seed is the rendezvous seed used by the current account | ### ContactRequestResetReference.Request ### ContactRequestSend ### ContactRequestSend.Reply ### ContactRequestSend.Request | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | contact | [ShareableContact](#weshnet-protocol-v1-ShareableContact) | | contact is a message describing how to connect to the other account | | own_metadata | [bytes](#bytes) | | own_metadata is the identifying metadata that will be shared to the other account | ### ContactUnblock ### ContactUnblock.Reply ### ContactUnblock.Request | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | contact_pk | [bytes](#bytes) | | contact_pk is the identifier of the contact to unblock | ### CredentialVerificationServiceCompleteFlow ### CredentialVerificationServiceCompleteFlow.Reply | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | identifier | [string](#string) | | | ### CredentialVerificationServiceCompleteFlow.Request | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | callback_uri | [string](#string) | | | ### CredentialVerificationServiceInitFlow ### CredentialVerificationServiceInitFlow.Reply | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | url | [string](#string) | | | | secure_url | [bool](#bool) | | | ### CredentialVerificationServiceInitFlow.Request | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | service_url | [string](#string) | | | | public_key | [bytes](#bytes) | | | | link | [string](#string) | | | ### DeactivateGroup ### DeactivateGroup.Reply ### DeactivateGroup.Request | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | group_pk | [bytes](#bytes) | | group_pk is the identifier of the group | ### DebugGroup ### DebugGroup.Reply | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | peer_ids | [string](#string) | repeated | peer_ids is the list of peer ids connected to the same group | ### DebugGroup.Request | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | group_pk | [bytes](#bytes) | | group_pk is the identifier of the group | ### DebugInspectGroupStore ### DebugInspectGroupStore.Reply | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | cid | [bytes](#bytes) | | cid is the CID of the IPFS log entry | | parent_cids | [bytes](#bytes) | repeated | parent_cids is the list of the parent entries | | metadata_event_type | [EventType](#weshnet-protocol-v1-EventType) | | event_type metadata event type if subscribed to metadata events | | device_pk | [bytes](#bytes) | | device_pk is the public key of the device signing the entry | | payload | [bytes](#bytes) | | payload is the un encrypted entry payload if available | ### DebugInspectGroupStore.Request | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | group_pk | [bytes](#bytes) | | group_pk is the identifier of the group | | log_type | [DebugInspectGroupLogType](#weshnet-protocol-v1-DebugInspectGroupLogType) | | log_type is the log to inspect | ### DebugListGroups ### DebugListGroups.Reply | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | group_pk | [bytes](#bytes) | | group_pk is the public key of the group | | group_type | [GroupType](#weshnet-protocol-v1-GroupType) | | group_type is the type of the group | | contact_pk | [bytes](#bytes) | | contact_pk is the contact public key if appropriate | ### DebugListGroups.Request ### DecodeContact ### DecodeContact.Reply | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | contact | [ShareableContact](#weshnet-protocol-v1-ShareableContact) | | shareable_contact is the decoded shareable contact. | ### DecodeContact.Request | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | encoded_contact | [bytes](#bytes) | | encoded_contact is the Protobuf encoding of the shareable contact (as returned by ShareContact). | ### DeviceChainKey DeviceChainKey is a chain key, which will be encrypted for a specific member of the group | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | chain_key | [bytes](#bytes) | | chain_key is the current value of the chain key of the group device | | counter | [uint64](#uint64) | | counter is the current value of the counter of the group device | ### EncryptedMessage EncryptedMessage is used in MessageEnvelope and only readable by groups members that joined before the message was sent | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | plaintext | [bytes](#bytes) | | plaintext is the app layer data | | protocol_metadata | [ProtocolMetadata](#weshnet-protocol-v1-ProtocolMetadata) | | protocol_metadata is protocol layer data | ### EventContext EventContext adds context (its id, its parents and its attachments) to an event | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | id | [bytes](#bytes) | | id is the CID of the underlying OrbitDB event | | parent_ids | [bytes](#bytes) | repeated | id are the the CIDs of the underlying parents of the OrbitDB event | | group_pk | [bytes](#bytes) | | group_pk receiving the event | ### FirstLastCounters | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | first | [uint64](#uint64) | | | | last | [uint64](#uint64) | | | ### Group Group define a group and is enough to invite someone to it | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | 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 | | secret | [bytes](#bytes) | | secret is the symmetric secret of the group, which is used to encrypt the metadata | | secret_sig | [bytes](#bytes) | | secret_sig is the signature of the secret used to ensure the validity of the group | | group_type | [GroupType](#weshnet-protocol-v1-GroupType) | | group_type specifies the type of the group, used to determine how device chain key is generated | | sign_pub | [bytes](#bytes) | | sign_pub is the signature public key used to verify entries, not required when secret and secret_sig are provided | | link_key | [bytes](#bytes) | | link_key is the secret key used to exchange group updates and links to attachments, useful for replication services | | link_key_sig | [bytes](#bytes) | | link_key_sig is the signature of the link_key using the group private key | ### GroupAddAdditionalRendezvousSeed GroupAddAdditionalRendezvousSeed indicates that an additional rendezvous point should be used for data synchronization | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | 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 | | seed | [bytes](#bytes) | | seed is the additional rendezvous point seed which should be used | ### GroupDeviceChainKeyAdded GroupDeviceChainKeyAdded is an event which indicates to a group member a device chain key | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | device_pk | [bytes](#bytes) | | device_pk is the device sending the event, signs the message | | dest_member_pk | [bytes](#bytes) | | dest_member_pk is the member who should receive the secret | | payload | [bytes](#bytes) | | payload is the serialization of Payload encrypted for the specified member | ### GroupDeviceStatus ### GroupDeviceStatus.Reply | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | type | [GroupDeviceStatus.Type](#weshnet-protocol-v1-GroupDeviceStatus-Type) | | | | event | [bytes](#bytes) | | | ### GroupDeviceStatus.Reply.PeerConnected | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | peer_id | [string](#string) | | | | device_pk | [bytes](#bytes) | | | | transports | [GroupDeviceStatus.Transport](#weshnet-protocol-v1-GroupDeviceStatus-Transport) | repeated | | | maddrs | [string](#string) | repeated | | ### GroupDeviceStatus.Reply.PeerDisconnected | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | peer_id | [string](#string) | | | ### GroupDeviceStatus.Reply.PeerReconnecting | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | peer_id | [string](#string) | | | ### GroupDeviceStatus.Request | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | group_pk | [bytes](#bytes) | | | ### GroupEnvelope GroupEnvelope is a publicly exposed structure containing a group metadata event | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | nonce | [bytes](#bytes) | | nonce is used to encrypt the message | | event | [bytes](#bytes) | | event is encrypted using a symmetric key shared among group members | ### GroupHeadsExport | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | 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 | | sign_pub | [bytes](#bytes) | | sign_pub is the signature public key used to verify entries | | metadata_heads_cids | [bytes](#bytes) | repeated | metadata_heads_cids are the heads of the metadata store that should be restored from an export | | messages_heads_cids | [bytes](#bytes) | repeated | messages_heads_cids are the heads of the metadata store that should be restored from an export | | link_key | [bytes](#bytes) | | link_key | ### GroupInfo ### GroupInfo.Reply | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | group | [Group](#weshnet-protocol-v1-Group) | | group is the group invitation, containing the group pk and its type | | member_pk | [bytes](#bytes) | | member_pk is the identifier of the current member in the group | | device_pk | [bytes](#bytes) | | device_pk is the identifier of the current device in the group | ### GroupInfo.Request | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | group_pk | [bytes](#bytes) | | group_pk is the identifier of the group | | contact_pk | [bytes](#bytes) | | contact_pk is the identifier of the contact | ### GroupMemberDeviceAdded GroupMemberDeviceAdded is an event which indicates to a group a new device (and eventually a new member) is joining it When added on AccountGroup, this event should be followed by appropriate GroupMemberDeviceAdded and GroupDeviceChainKeyAdded events | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | member_pk | [bytes](#bytes) | | member_pk is the member sending the event | | device_pk | [bytes](#bytes) | | device_pk is the device sending the event, signs the message | | member_sig | [bytes](#bytes) | | member_sig is used to prove the ownership of the member pk TODO: signature of what ??? ensure it can't be replayed | ### GroupMessageEvent | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | event_context | [EventContext](#weshnet-protocol-v1-EventContext) | | event_context contains context information about the event | | headers | [MessageHeaders](#weshnet-protocol-v1-MessageHeaders) | | headers contains headers of the secure message | | message | [bytes](#bytes) | | message contains the secure message payload | ### GroupMessageList ### GroupMessageList.Request | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | group_pk | [bytes](#bytes) | | group_pk is the identifier of the group | | since_id | [bytes](#bytes) | | since is the lower ID bound used to filter events if not set, will return events since the beginning | | since_now | [bool](#bool) | | since_now will list only new event to come since_id must not be set | | until_id | [bytes](#bytes) | | until is the upper ID bound used to filter events if not set, will subscribe to new events to come | | until_now | [bool](#bool) | | until_now will not list new event to come until_id must not be set | | reverse_order | [bool](#bool) | | reverse_order indicates whether the previous events should be returned in reverse chronological order | ### GroupMetadata GroupMetadata is used in GroupEnvelope and only readable by invited group members | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | event_type | [EventType](#weshnet-protocol-v1-EventType) | | event_type defines which event type is used | | payload | [bytes](#bytes) | | the serialization depends on event_type, event is symmetrically encrypted | | sig | [bytes](#bytes) | | sig is the signature of the payload, it depends on the event_type for the used key | | protocol_metadata | [ProtocolMetadata](#weshnet-protocol-v1-ProtocolMetadata) | | protocol_metadata is protocol layer data | ### GroupMetadataEvent | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | event_context | [EventContext](#weshnet-protocol-v1-EventContext) | | event_context contains context information about the event | | metadata | [GroupMetadata](#weshnet-protocol-v1-GroupMetadata) | | metadata contains the newly available metadata | | event | [bytes](#bytes) | | event_clear clear bytes for the event | ### GroupMetadataList ### GroupMetadataList.Request | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | group_pk | [bytes](#bytes) | | group_pk is the identifier of the group | | since_id | [bytes](#bytes) | | since is the lower ID bound used to filter events if not set, will return events since the beginning | | since_now | [bool](#bool) | | since_now will list only new event to come since_id must not be set | | until_id | [bytes](#bytes) | | until is the upper ID bound used to filter events if not set, will subscribe to new events to come | | until_now | [bool](#bool) | | until_now will not list new event to come until_id must not be set | | reverse_order | [bool](#bool) | | reverse_order indicates whether the previous events should be returned in reverse chronological order | ### GroupMetadataPayloadSent GroupMetadataPayloadSent is an app defined message, accessible to future group members | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | device_pk | [bytes](#bytes) | | device_pk is the device sending the event, signs the message | | message | [bytes](#bytes) | | message is the payload | ### GroupRemoveAdditionalRendezvousSeed GroupRemoveAdditionalRendezvousSeed indicates that a previously added rendezvous point should be removed | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | 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 | | seed | [bytes](#bytes) | | seed is the additional rendezvous point seed which should be removed | ### GroupReplicating | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | device_pk | [bytes](#bytes) | | device_pk is the device sending the event, signs the message | | authentication_url | [string](#string) | | authentication_url indicates which server has been used for authentication | | replication_server | [string](#string) | | replication_server indicates which server will be used for replication | ### MessageEnvelope MessageEnvelope is a publicly exposed structure containing a group secure message | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | message_headers | [bytes](#bytes) | | message_headers is an encrypted serialization using a symmetric key of a MessageHeaders message | | message | [bytes](#bytes) | | message is an encrypted message, only readable by group members who previously received the appropriate chain key | | nonce | [bytes](#bytes) | | nonce is a nonce for message headers | ### MessageHeaders MessageHeaders is used in MessageEnvelope and only readable by invited group members | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | counter | [uint64](#uint64) | | counter is the current counter value for the specified device | | device_pk | [bytes](#bytes) | | device_pk is the public key of the device sending the message | | sig | [bytes](#bytes) | | sig is the signature of the encrypted message using the device's private key | | metadata | [MessageHeaders.MetadataEntry](#weshnet-protocol-v1-MessageHeaders-MetadataEntry) | repeated | metadata allow to pass custom informations | ### MessageHeaders.MetadataEntry | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | key | [string](#string) | | | | value | [string](#string) | | | ### MultiMemberGroupAdminRoleGrant ### MultiMemberGroupAdminRoleGrant.Reply ### MultiMemberGroupAdminRoleGrant.Request | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | group_pk | [bytes](#bytes) | | group_pk is the identifier of the group | | member_pk | [bytes](#bytes) | | member_pk is the identifier of the member which will be granted the admin role | ### MultiMemberGroupAdminRoleGranted MultiMemberGroupAdminRoleGranted indicates that a group admin allows another group member to act as an admin | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | 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 | | grantee_member_pk | [bytes](#bytes) | | grantee_member_pk is the member public key of the member granted of the admin role | ### MultiMemberGroupAliasResolverAdded MultiMemberGroupAliasResolverAdded indicates that a group member want to disclose their presence in the group to their contacts | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | device_pk | [bytes](#bytes) | | device_pk is the device sending the event, signs the message | | 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) | | alias_proof | [bytes](#bytes) | | alias_proof ensures that the associated alias_resolver has been issued by the right account Generated using aliasSKSig(GroupID) | ### MultiMemberGroupAliasResolverDisclose ### MultiMemberGroupAliasResolverDisclose.Reply ### MultiMemberGroupAliasResolverDisclose.Request | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | group_pk | [bytes](#bytes) | | group_pk is the identifier of the group | ### MultiMemberGroupCreate ### MultiMemberGroupCreate.Reply | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | group_pk | [bytes](#bytes) | | group_pk is the identifier of the newly created group | ### MultiMemberGroupCreate.Request ### MultiMemberGroupInitialMemberAnnounced MultiMemberGroupInitialMemberAnnounced indicates that a member is the group creator, this event is signed using the group ID private key | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | member_pk | [bytes](#bytes) | | member_pk is the public key of the member who is the group creator | ### MultiMemberGroupInvitationCreate ### MultiMemberGroupInvitationCreate.Reply | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | group | [Group](#weshnet-protocol-v1-Group) | | group is the invitation to the group | ### MultiMemberGroupInvitationCreate.Request | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | group_pk | [bytes](#bytes) | | group_pk is the identifier of the group | ### MultiMemberGroupJoin ### MultiMemberGroupJoin.Reply ### MultiMemberGroupJoin.Request | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | group | [Group](#weshnet-protocol-v1-Group) | | group is the information of the group to join | ### MultiMemberGroupLeave ### MultiMemberGroupLeave.Reply ### MultiMemberGroupLeave.Request | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | group_pk | [bytes](#bytes) | | | ### OrbitDBMessageHeads OrbitDBMessageHeads is the payload sent on orbitdb to share peer's heads | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | sealed_box | [bytes](#bytes) | | sealed box should contain encrypted Box | | raw_rotation | [bytes](#bytes) | | current topic used | ### OrbitDBMessageHeads.Box | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | address | [string](#string) | | | | heads | [bytes](#bytes) | | | | device_pk | [bytes](#bytes) | | | | peer_id | [bytes](#bytes) | | | ### OutOfStoreMessage | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | cid | [bytes](#bytes) | | | | device_pk | [bytes](#bytes) | | | | counter | [fixed64](#fixed64) | | | | sig | [bytes](#bytes) | | | | flags | [fixed32](#fixed32) | | | | encrypted_payload | [bytes](#bytes) | | | | nonce | [bytes](#bytes) | | | ### OutOfStoreMessageEnvelope | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | nonce | [bytes](#bytes) | | | | box | [bytes](#bytes) | | | | group_reference | [bytes](#bytes) | | | ### OutOfStoreReceive ### OutOfStoreReceive.Reply | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | message | [OutOfStoreMessage](#weshnet-protocol-v1-OutOfStoreMessage) | | | | cleartext | [bytes](#bytes) | | | | group_public_key | [bytes](#bytes) | | | | already_received | [bool](#bool) | | | ### OutOfStoreReceive.Request | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | payload | [bytes](#bytes) | | | ### OutOfStoreSeal ### OutOfStoreSeal.Reply | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | encrypted | [bytes](#bytes) | | | ### OutOfStoreSeal.Request | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | cid | [bytes](#bytes) | | | | group_public_key | [bytes](#bytes) | | | ### PeerList ### PeerList.Peer | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | id | [string](#string) | | id is the libp2p.PeerID. | | routes | [PeerList.Route](#weshnet-protocol-v1-PeerList-Route) | repeated | routes are the list of active and known maddr. | | errors | [string](#string) | repeated | errors is a list of errors related to the peer. | | features | [PeerList.Feature](#weshnet-protocol-v1-PeerList-Feature) | repeated | Features is a list of available features. | | min_latency | [int64](#int64) | | MinLatency is the minimum latency across all the peer routes. | | is_active | [bool](#bool) | | IsActive is true if at least one of the route is active. | | direction | [Direction](#weshnet-protocol-v1-Direction) | | Direction is the aggregate of all the routes's direction. | ### PeerList.Reply | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | peers | [PeerList.Peer](#weshnet-protocol-v1-PeerList-Peer) | repeated | | ### PeerList.Request ### PeerList.Route | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | is_active | [bool](#bool) | | IsActive indicates whether the address is currently used or just known. | | address | [string](#string) | | Address is the multiaddress via which we are connected with the peer. | | direction | [Direction](#weshnet-protocol-v1-Direction) | | Direction is which way the connection was established. | | latency | [int64](#int64) | | Latency is the last known round trip time to the peer in ms. | | streams | [PeerList.Stream](#weshnet-protocol-v1-PeerList-Stream) | repeated | Streams returns list of streams established with the peer. | ### PeerList.Stream | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | id | [string](#string) | | id is an identifier used to write protocol headers in streams. | ### Progress Progress define a generic object that can be used to display a progress bar for long-running actions. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | state | [string](#string) | | | | doing | [string](#string) | | | | progress | [float](#float) | | | | completed | [uint64](#uint64) | | | | total | [uint64](#uint64) | | | | delay | [uint64](#uint64) | | | ### ProtocolMetadata ### RefreshContactRequest ### RefreshContactRequest.Peer | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | id | [string](#string) | | id is the libp2p.PeerID. | | addrs | [string](#string) | repeated | list of peers multiaddrs. | ### RefreshContactRequest.Reply | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | peers_found | [RefreshContactRequest.Peer](#weshnet-protocol-v1-RefreshContactRequest-Peer) | repeated | peers found and successfully connected. | ### RefreshContactRequest.Request | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | contact_pk | [bytes](#bytes) | | | | timeout | [int64](#int64) | | timeout in second | ### ReplicationServiceRegisterGroup ### ReplicationServiceRegisterGroup.Reply ### ReplicationServiceRegisterGroup.Request | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | group_pk | [bytes](#bytes) | | | | token | [string](#string) | | | | authentication_url | [string](#string) | | | | replication_server | [string](#string) | | | ### ReplicationServiceReplicateGroup ### ReplicationServiceReplicateGroup.Reply | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | ok | [bool](#bool) | | | ### ReplicationServiceReplicateGroup.Request | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | group | [Group](#weshnet-protocol-v1-Group) | | | ### ServiceExportData ### ServiceExportData.Reply | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | exported_data | [bytes](#bytes) | | | ### ServiceExportData.Request ### ServiceGetConfiguration ### ServiceGetConfiguration.Reply | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | account_pk | [bytes](#bytes) | | account_pk is the public key of the current account | | device_pk | [bytes](#bytes) | | device_pk is the public key of the current device | | account_group_pk | [bytes](#bytes) | | account_group_pk is the public key of the account group | | peer_id | [string](#string) | | peer_id is the peer ID of the current IPFS node | | listeners | [string](#string) | repeated | listeners is the list of swarm listening addresses of the current IPFS node | | ble_enabled | [ServiceGetConfiguration.SettingState](#weshnet-protocol-v1-ServiceGetConfiguration-SettingState) | | | | wifi_p2p_enabled | [ServiceGetConfiguration.SettingState](#weshnet-protocol-v1-ServiceGetConfiguration-SettingState) | | MultiPeerConnectivity for Darwin and Nearby for Android | | mdns_enabled | [ServiceGetConfiguration.SettingState](#weshnet-protocol-v1-ServiceGetConfiguration-SettingState) | | | | relay_enabled | [ServiceGetConfiguration.SettingState](#weshnet-protocol-v1-ServiceGetConfiguration-SettingState) | | | ### ServiceGetConfiguration.Request ### ServiceToken | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | token | [string](#string) | | | | authentication_url | [string](#string) | | | | supported_services | [ServiceTokenSupportedService](#weshnet-protocol-v1-ServiceTokenSupportedService) | repeated | | | expiration | [int64](#int64) | | | ### ServiceTokenSupportedService | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | service_type | [string](#string) | | | | service_endpoint | [string](#string) | | | ### ShareContact ### ShareContact.Reply | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | 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. | ### ShareContact.Request ### ShareableContact | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | pk | [bytes](#bytes) | | pk is the account to send a contact request to | | public_rendezvous_seed | [bytes](#bytes) | | public_rendezvous_seed is the rendezvous seed used by the account to send a contact request to | | metadata | [bytes](#bytes) | | metadata is the metadata specific to the app to identify the contact for the request | ### SystemInfo ### SystemInfo.OrbitDB | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | account_metadata | [SystemInfo.OrbitDB.ReplicationStatus](#weshnet-protocol-v1-SystemInfo-OrbitDB-ReplicationStatus) | | | ### SystemInfo.OrbitDB.ReplicationStatus | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | progress | [int64](#int64) | | | | maximum | [int64](#int64) | | | | buffered | [int64](#int64) | | | | queued | [int64](#int64) | | | ### SystemInfo.P2P | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | connected_peers | [int64](#int64) | | | ### SystemInfo.Process | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | version | [string](#string) | | | | vcs_ref | [string](#string) | | | | uptime_ms | [int64](#int64) | | | | user_cpu_time_ms | [int64](#int64) | | | | system_cpu_time_ms | [int64](#int64) | | | | started_at | [int64](#int64) | | | | rlimit_cur | [uint64](#uint64) | | | | num_goroutine | [int64](#int64) | | | | nofile | [int64](#int64) | | | | too_many_open_files | [bool](#bool) | | | | num_cpu | [int64](#int64) | | | | go_version | [string](#string) | | | | operating_system | [string](#string) | | | | host_name | [string](#string) | | | | arch | [string](#string) | | | | rlimit_max | [uint64](#uint64) | | | | pid | [int64](#int64) | | | | ppid | [int64](#int64) | | | | priority | [int64](#int64) | | | | uid | [int64](#int64) | | | | working_dir | [string](#string) | | | | system_username | [string](#string) | | | ### SystemInfo.Reply | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | process | [SystemInfo.Process](#weshnet-protocol-v1-SystemInfo-Process) | | | | p2p | [SystemInfo.P2P](#weshnet-protocol-v1-SystemInfo-P2P) | | | | orbitdb | [SystemInfo.OrbitDB](#weshnet-protocol-v1-SystemInfo-OrbitDB) | | | | warns | [string](#string) | repeated | | ### SystemInfo.Request ### VerifiedCredentialsList ### VerifiedCredentialsList.Reply | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | credential | [AccountVerifiedCredentialRegistered](#weshnet-protocol-v1-AccountVerifiedCredentialRegistered) | | | ### VerifiedCredentialsList.Request | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | filter_identifier | [string](#string) | | | | filter_issuer | [string](#string) | | | | exclude_expired | [bool](#bool) | | | ### ContactState | Name | Number | Description | | ---- | ------ | ----------- | | ContactStateUndefined | 0 | | | ContactStateToRequest | 1 | | | ContactStateReceived | 2 | | | ContactStateAdded | 3 | | | ContactStateRemoved | 4 | | | ContactStateDiscarded | 5 | | | ContactStateBlocked | 6 | | ### DebugInspectGroupLogType | Name | Number | Description | | ---- | ------ | ----------- | | DebugInspectGroupLogTypeUndefined | 0 | | | DebugInspectGroupLogTypeMessage | 1 | | | DebugInspectGroupLogTypeMetadata | 2 | | ### Direction | Name | Number | Description | | ---- | ------ | ----------- | | UnknownDir | 0 | | | InboundDir | 1 | | | OutboundDir | 2 | | | BiDir | 3 | | ### EventType | Name | Number | Description | | ---- | ------ | ----------- | | EventTypeUndefined | 0 | EventTypeUndefined indicates that the value has not been set. Should not happen. | | EventTypeGroupMemberDeviceAdded | 1 | EventTypeGroupMemberDeviceAdded indicates the payload includes that a member has added their device to the group | | EventTypeGroupDeviceChainKeyAdded | 2 | EventTypeGroupDeviceChainKeyAdded indicates the payload includes that a member has sent their device chain key to another member | | EventTypeAccountGroupJoined | 101 | EventTypeAccountGroupJoined indicates the payload includes that the account has joined a group | | EventTypeAccountGroupLeft | 102 | EventTypeAccountGroupLeft indicates the payload includes that the account has left a group | | EventTypeAccountContactRequestDisabled | 103 | EventTypeAccountContactRequestDisabled indicates the payload includes that the account has disabled incoming contact requests | | EventTypeAccountContactRequestEnabled | 104 | EventTypeAccountContactRequestEnabled indicates the payload includes that the account has enabled incoming contact requests | | EventTypeAccountContactRequestReferenceReset | 105 | EventTypeAccountContactRequestReferenceReset indicates the payload includes that the account has a new contact request rendezvous seed | | EventTypeAccountContactRequestOutgoingEnqueued | 106 | EventTypeAccountContactRequestOutgoingEnqueued indicates the payload includes that the account will attempt to send a new contact request | | EventTypeAccountContactRequestOutgoingSent | 107 | EventTypeAccountContactRequestOutgoingSent indicates the payload includes that the account has sent a contact request | | EventTypeAccountContactRequestIncomingReceived | 108 | EventTypeAccountContactRequestIncomingReceived indicates the payload includes that the account has received a contact request | | EventTypeAccountContactRequestIncomingDiscarded | 109 | EventTypeAccountContactRequestIncomingDiscarded indicates the payload includes that the account has ignored a contact request | | EventTypeAccountContactRequestIncomingAccepted | 110 | EventTypeAccountContactRequestIncomingAccepted indicates the payload includes that the account has accepted a contact request | | EventTypeAccountContactBlocked | 111 | EventTypeAccountContactBlocked indicates the payload includes that the account has blocked a contact | | EventTypeAccountContactUnblocked | 112 | EventTypeAccountContactUnblocked indicates the payload includes that the account has unblocked a contact | | EventTypeContactAliasKeyAdded | 201 | EventTypeContactAliasKeyAdded indicates the payload includes that the contact group has received an alias key | | EventTypeMultiMemberGroupAliasResolverAdded | 301 | EventTypeMultiMemberGroupAliasResolverAdded indicates the payload includes that a member of the group sent their alias proof | | EventTypeMultiMemberGroupInitialMemberAnnounced | 302 | EventTypeMultiMemberGroupInitialMemberAnnounced indicates the payload includes that a member has authenticated themselves as the group owner | | EventTypeMultiMemberGroupAdminRoleGranted | 303 | EventTypeMultiMemberGroupAdminRoleGranted indicates the payload includes that an admin of the group granted another member as an admin | | EventTypeGroupReplicating | 403 | EventTypeGroupReplicating indicates that the group has been registered for replication on a server | | EventTypeAccountVerifiedCredentialRegistered | 500 | EventTypeAccountVerifiedCredentialRegistered | | 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 | ### GroupDeviceStatus.Transport | Name | Number | Description | | ---- | ------ | ----------- | | TptUnknown | 0 | | | TptLAN | 1 | | | TptWAN | 2 | | | TptProximity | 3 | | ### GroupDeviceStatus.Type | Name | Number | Description | | ---- | ------ | ----------- | | TypeUnknown | 0 | | | TypePeerDisconnected | 1 | | | TypePeerConnected | 2 | | | TypePeerReconnecting | 3 | | ### GroupType | Name | Number | Description | | ---- | ------ | ----------- | | GroupTypeUndefined | 0 | GroupTypeUndefined indicates that the value has not been set. For example, happens if group is replicated. | | GroupTypeAccount | 1 | GroupTypeAccount is the group managing an account, available to all its devices. | | GroupTypeContact | 2 | GroupTypeContact is the group created between two accounts, available to all their devices. | | GroupTypeMultiMember | 3 | GroupTypeMultiMember is a group containing an undefined number of members. | ### PeerList.Feature | Name | Number | Description | | ---- | ------ | ----------- | | UnknownFeature | 0 | | | WeshFeature | 1 | | | BLEFeature | 2 | | | LocalFeature | 3 | | | TorFeature | 4 | | | QuicFeature | 5 | | ### ServiceGetConfiguration.SettingState | Name | Number | Description | | ---- | ------ | ----------- | | Unknown | 0 | | | Enabled | 1 | | | Disabled | 2 | | | Unavailable | 3 | | ### ProtocolService ProtocolService is the top-level API to manage the Wesh protocol service. Each active Wesh protocol service is considered as a Wesh device and is associated with a Wesh user. | Method Name | Request Type | Response Type | Description | | ----------- | ------------ | ------------- | ------------| | 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 | | ServiceGetConfiguration | [ServiceGetConfiguration.Request](#weshnet-protocol-v1-ServiceGetConfiguration-Request) | [ServiceGetConfiguration.Reply](#weshnet-protocol-v1-ServiceGetConfiguration-Reply) | ServiceGetConfiguration gets the current configuration of the protocol service | | 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 | | ContactRequestDisable | [ContactRequestDisable.Request](#weshnet-protocol-v1-ContactRequestDisable-Request) | [ContactRequestDisable.Reply](#weshnet-protocol-v1-ContactRequestDisable-Reply) | ContactRequestDisable disables incoming contact requests | | ContactRequestEnable | [ContactRequestEnable.Request](#weshnet-protocol-v1-ContactRequestEnable-Request) | [ContactRequestEnable.Reply](#weshnet-protocol-v1-ContactRequestEnable-Reply) | ContactRequestEnable enables incoming contact requests | | ContactRequestResetReference | [ContactRequestResetReference.Request](#weshnet-protocol-v1-ContactRequestResetReference-Request) | [ContactRequestResetReference.Reply](#weshnet-protocol-v1-ContactRequestResetReference-Reply) | ContactRequestResetReference changes the contact request reference | | ContactRequestSend | [ContactRequestSend.Request](#weshnet-protocol-v1-ContactRequestSend-Request) | [ContactRequestSend.Reply](#weshnet-protocol-v1-ContactRequestSend-Reply) | ContactRequestSend attempt to send a contact request | | ContactRequestAccept | [ContactRequestAccept.Request](#weshnet-protocol-v1-ContactRequestAccept-Request) | [ContactRequestAccept.Reply](#weshnet-protocol-v1-ContactRequestAccept-Reply) | ContactRequestAccept accepts a contact request | | 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 | | 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. | | 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. | | ContactBlock | [ContactBlock.Request](#weshnet-protocol-v1-ContactBlock-Request) | [ContactBlock.Reply](#weshnet-protocol-v1-ContactBlock-Reply) | ContactBlock blocks a contact from sending requests | | ContactUnblock | [ContactUnblock.Request](#weshnet-protocol-v1-ContactUnblock-Request) | [ContactUnblock.Reply](#weshnet-protocol-v1-ContactUnblock-Reply) | ContactUnblock unblocks a contact from sending requests | | 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 | | MultiMemberGroupCreate | [MultiMemberGroupCreate.Request](#weshnet-protocol-v1-MultiMemberGroupCreate-Request) | [MultiMemberGroupCreate.Reply](#weshnet-protocol-v1-MultiMemberGroupCreate-Reply) | MultiMemberGroupCreate creates a new multi-member group | | MultiMemberGroupJoin | [MultiMemberGroupJoin.Request](#weshnet-protocol-v1-MultiMemberGroupJoin-Request) | [MultiMemberGroupJoin.Reply](#weshnet-protocol-v1-MultiMemberGroupJoin-Reply) | MultiMemberGroupJoin joins a multi-member group | | MultiMemberGroupLeave | [MultiMemberGroupLeave.Request](#weshnet-protocol-v1-MultiMemberGroupLeave-Request) | [MultiMemberGroupLeave.Reply](#weshnet-protocol-v1-MultiMemberGroupLeave-Reply) | MultiMemberGroupLeave leaves a multi-member group | | MultiMemberGroupAliasResolverDisclose | [MultiMemberGroupAliasResolverDisclose.Request](#weshnet-protocol-v1-MultiMemberGroupAliasResolverDisclose-Request) | [MultiMemberGroupAliasResolverDisclose.Reply](#weshnet-protocol-v1-MultiMemberGroupAliasResolverDisclose-Reply) | MultiMemberGroupAliasResolverDisclose discloses your alias resolver key | | MultiMemberGroupAdminRoleGrant | [MultiMemberGroupAdminRoleGrant.Request](#weshnet-protocol-v1-MultiMemberGroupAdminRoleGrant-Request) | [MultiMemberGroupAdminRoleGrant.Reply](#weshnet-protocol-v1-MultiMemberGroupAdminRoleGrant-Reply) | MultiMemberGroupAdminRoleGrant grants an admin role to a group member | | MultiMemberGroupInvitationCreate | [MultiMemberGroupInvitationCreate.Request](#weshnet-protocol-v1-MultiMemberGroupInvitationCreate-Request) | [MultiMemberGroupInvitationCreate.Reply](#weshnet-protocol-v1-MultiMemberGroupInvitationCreate-Reply) | MultiMemberGroupInvitationCreate creates an invitation to a multi-member group | | 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 | | 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 | | 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 | | 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 | | GroupInfo | [GroupInfo.Request](#weshnet-protocol-v1-GroupInfo-Request) | [GroupInfo.Reply](#weshnet-protocol-v1-GroupInfo-Reply) | GroupInfo retrieves information about a group | | ActivateGroup | [ActivateGroup.Request](#weshnet-protocol-v1-ActivateGroup-Request) | [ActivateGroup.Reply](#weshnet-protocol-v1-ActivateGroup-Reply) | ActivateGroup explicitly opens a group | | DeactivateGroup | [DeactivateGroup.Request](#weshnet-protocol-v1-DeactivateGroup-Request) | [DeactivateGroup.Reply](#weshnet-protocol-v1-DeactivateGroup-Reply) | DeactivateGroup closes a group | | GroupDeviceStatus | [GroupDeviceStatus.Request](#weshnet-protocol-v1-GroupDeviceStatus-Request) | [GroupDeviceStatus.Reply](#weshnet-protocol-v1-GroupDeviceStatus-Reply) stream | GroupDeviceStatus monitor device status | | DebugListGroups | [DebugListGroups.Request](#weshnet-protocol-v1-DebugListGroups-Request) | [DebugListGroups.Reply](#weshnet-protocol-v1-DebugListGroups-Reply) stream | | | DebugInspectGroupStore | [DebugInspectGroupStore.Request](#weshnet-protocol-v1-DebugInspectGroupStore-Request) | [DebugInspectGroupStore.Reply](#weshnet-protocol-v1-DebugInspectGroupStore-Reply) stream | | | DebugGroup | [DebugGroup.Request](#weshnet-protocol-v1-DebugGroup-Request) | [DebugGroup.Reply](#weshnet-protocol-v1-DebugGroup-Reply) | | | SystemInfo | [SystemInfo.Request](#weshnet-protocol-v1-SystemInfo-Request) | [SystemInfo.Reply](#weshnet-protocol-v1-SystemInfo-Reply) | | | CredentialVerificationServiceInitFlow | [CredentialVerificationServiceInitFlow.Request](#weshnet-protocol-v1-CredentialVerificationServiceInitFlow-Request) | [CredentialVerificationServiceInitFlow.Reply](#weshnet-protocol-v1-CredentialVerificationServiceInitFlow-Reply) | CredentialVerificationServiceInitFlow Initialize a credential verification flow | | CredentialVerificationServiceCompleteFlow | [CredentialVerificationServiceCompleteFlow.Request](#weshnet-protocol-v1-CredentialVerificationServiceCompleteFlow-Request) | [CredentialVerificationServiceCompleteFlow.Reply](#weshnet-protocol-v1-CredentialVerificationServiceCompleteFlow-Reply) | CredentialVerificationServiceCompleteFlow Completes a credential verification flow | | VerifiedCredentialsList | [VerifiedCredentialsList.Request](#weshnet-protocol-v1-VerifiedCredentialsList-Request) | [VerifiedCredentialsList.Reply](#weshnet-protocol-v1-VerifiedCredentialsList-Reply) stream | VerifiedCredentialsList Retrieves the list of verified credentials | | 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 | | PeerList | [PeerList.Request](#weshnet-protocol-v1-PeerList-Request) | [PeerList.Reply](#weshnet-protocol-v1-PeerList-Reply) | PeerList returns a list of P2P peers | | OutOfStoreReceive | [OutOfStoreReceive.Request](#weshnet-protocol-v1-OutOfStoreReceive-Request) | [OutOfStoreReceive.Reply](#weshnet-protocol-v1-OutOfStoreReceive-Reply) | OutOfStoreReceive parses a payload received outside a synchronized store | | 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 | | 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 | ## Scalar Value Types | .proto Type | Notes | C++ | Java | Python | Go | C# | PHP | Ruby | | ----------- | ----- | --- | ---- | ------ | -- | -- | --- | ---- | | double | | double | double | float | float64 | double | float | Float | | float | | float | float | float | float32 | float | float | Float | | 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) | | 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 | | uint32 | Uses variable-length encoding. | uint32 | int | int/long | uint32 | uint | integer | Bignum or Fixnum (as required) | | uint64 | Uses variable-length encoding. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum or Fixnum (as required) | | 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) | | 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 | | 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) | | 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 | | sfixed32 | Always four bytes. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) | | sfixed64 | Always eight bytes. | int64 | long | int/long | int64 | long | integer/string | Bignum | | bool | | bool | boolean | boolean | bool | bool | boolean | TrueClass/FalseClass | | string | A string must always contain UTF-8 encoded or 7-bit ASCII text. | string | String | str/unicode | string | string | string | String (UTF-8) | | bytes | May contain any arbitrary sequence of bytes. | string | ByteString | str | []byte | ByteString | string | String (ASCII-8BIT) | ================================================ FILE: docs/apis/protocoltypes.swagger.json ================================================ { "swagger": "2.0", "info": { "title": "protocoltypes.proto", "version": "version not set" }, "consumes": [ "application/json" ], "produces": [ "application/json" ], "paths": {}, "definitions": { "GroupDeviceStatusType": { "type": "string", "enum": [ "TypeUnknown", "TypePeerDisconnected", "TypePeerConnected", "TypePeerReconnecting" ], "default": "TypeUnknown" }, "OrbitDBReplicationStatus": { "type": "object", "properties": { "progress": { "type": "string", "format": "int64" }, "maximum": { "type": "string", "format": "int64" }, "buffered": { "type": "string", "format": "int64" }, "queued": { "type": "string", "format": "int64" } } }, "PeerListFeature": { "type": "string", "enum": [ "UnknownFeature", "WeshFeature", "BLEFeature", "LocalFeature", "TorFeature", "QuicFeature" ], "default": "UnknownFeature" }, "PeerListRoute": { "type": "object", "properties": { "is_active": { "type": "boolean", "description": "IsActive indicates whether the address is currently used or just known." }, "address": { "type": "string", "description": "Address is the multiaddress via which we are connected with the peer." }, "direction": { "$ref": "#/definitions/v1Direction", "description": "Direction is which way the connection was established." }, "latency": { "type": "string", "format": "int64", "description": "Latency is the last known round trip time to the peer in ms." }, "streams": { "type": "array", "items": { "$ref": "#/definitions/PeerListStream" }, "description": "Streams returns list of streams established with the peer." } } }, "PeerListStream": { "type": "object", "properties": { "id": { "type": "string", "description": "id is an identifier used to write protocol headers in streams." } } }, "ServiceGetConfigurationSettingState": { "type": "string", "enum": [ "Unknown", "Enabled", "Disabled", "Unavailable" ], "default": "Unknown" }, "SystemInfoOrbitDB": { "type": "object", "properties": { "account_metadata": { "$ref": "#/definitions/OrbitDBReplicationStatus" } } }, "SystemInfoP2P": { "type": "object", "properties": { "connected_peers": { "type": "string", "format": "int64" } } }, "SystemInfoProcess": { "type": "object", "properties": { "version": { "type": "string" }, "vcs_ref": { "type": "string" }, "uptime_ms": { "type": "string", "format": "int64" }, "user_cpu_time_ms": { "type": "string", "format": "int64" }, "system_cpu_time_ms": { "type": "string", "format": "int64" }, "started_at": { "type": "string", "format": "int64" }, "rlimit_cur": { "type": "string", "format": "uint64" }, "num_goroutine": { "type": "string", "format": "int64" }, "nofile": { "type": "string", "format": "int64" }, "too_many_open_files": { "type": "boolean" }, "num_cpu": { "type": "string", "format": "int64" }, "go_version": { "type": "string" }, "operating_system": { "type": "string" }, "host_name": { "type": "string" }, "arch": { "type": "string" }, "rlimit_max": { "type": "string", "format": "uint64" }, "pid": { "type": "string", "format": "int64" }, "ppid": { "type": "string", "format": "int64" }, "priority": { "type": "string", "format": "int64" }, "uid": { "type": "string", "format": "int64" }, "working_dir": { "type": "string" }, "system_username": { "type": "string" } } }, "protobufAny": { "type": "object", "properties": { "type_url": { "type": "string" }, "value": { "type": "string", "format": "byte" } } }, "runtimeError": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "integer", "format": "int32" }, "message": { "type": "string" }, "details": { "type": "array", "items": { "$ref": "#/definitions/protobufAny" } } } }, "runtimeStreamError": { "type": "object", "properties": { "grpc_code": { "type": "integer", "format": "int32" }, "http_code": { "type": "integer", "format": "int32" }, "message": { "type": "string" }, "http_status": { "type": "string" }, "details": { "type": "array", "items": { "$ref": "#/definitions/protobufAny" } } } }, "v1AccountVerifiedCredentialRegistered": { "type": "object", "properties": { "device_pk": { "type": "string", "format": "byte", "title": "device_pk is the public key of the device sending the message" }, "signed_identity_public_key": { "type": "string", "format": "byte" }, "verified_credential": { "type": "string" }, "registration_date": { "type": "string", "format": "int64" }, "expiration_date": { "type": "string", "format": "int64" }, "identifier": { "type": "string" }, "issuer": { "type": "string" } } }, "v1ActivateGroupReply": { "type": "object" }, "v1AppMessageSendReply": { "type": "object", "properties": { "cid": { "type": "string", "format": "byte" } } }, "v1AppMetadataSendReply": { "type": "object", "properties": { "cid": { "type": "string", "format": "byte" } } }, "v1ContactAliasKeySendReply": { "type": "object" }, "v1ContactBlockReply": { "type": "object" }, "v1ContactRequestAcceptReply": { "type": "object" }, "v1ContactRequestDisableReply": { "type": "object" }, "v1ContactRequestDiscardReply": { "type": "object" }, "v1ContactRequestEnableReply": { "type": "object", "properties": { "public_rendezvous_seed": { "type": "string", "format": "byte", "title": "public_rendezvous_seed is the rendezvous seed used by the current account" } } }, "v1ContactRequestReferenceReply": { "type": "object", "properties": { "public_rendezvous_seed": { "type": "string", "format": "byte", "title": "public_rendezvous_seed is the rendezvous seed used by the current account" }, "enabled": { "type": "boolean", "title": "enabled indicates if incoming contact requests are enabled" } } }, "v1ContactRequestResetReferenceReply": { "type": "object", "properties": { "public_rendezvous_seed": { "type": "string", "format": "byte", "title": "public_rendezvous_seed is the rendezvous seed used by the current account" } } }, "v1ContactRequestSendReply": { "type": "object" }, "v1ContactUnblockReply": { "type": "object" }, "v1CredentialVerificationServiceCompleteFlowReply": { "type": "object", "properties": { "identifier": { "type": "string" } } }, "v1CredentialVerificationServiceInitFlowReply": { "type": "object", "properties": { "url": { "type": "string" }, "secure_url": { "type": "boolean" } } }, "v1DeactivateGroupReply": { "type": "object" }, "v1DebugGroupReply": { "type": "object", "properties": { "peer_ids": { "type": "array", "items": { "type": "string" }, "title": "peer_ids is the list of peer ids connected to the same group" } } }, "v1DebugInspectGroupLogType": { "type": "string", "enum": [ "DebugInspectGroupLogTypeUndefined", "DebugInspectGroupLogTypeMessage", "DebugInspectGroupLogTypeMetadata" ], "default": "DebugInspectGroupLogTypeUndefined" }, "v1DebugInspectGroupStoreReply": { "type": "object", "properties": { "cid": { "type": "string", "format": "byte", "title": "cid is the CID of the IPFS log entry" }, "parent_cids": { "type": "array", "items": { "type": "string", "format": "byte" }, "title": "parent_cids is the list of the parent entries" }, "metadata_event_type": { "$ref": "#/definitions/v1EventType", "title": "event_type metadata event type if subscribed to metadata events" }, "device_pk": { "type": "string", "format": "byte", "title": "device_pk is the public key of the device signing the entry" }, "payload": { "type": "string", "format": "byte", "title": "payload is the un encrypted entry payload if available" } } }, "v1DebugListGroupsReply": { "type": "object", "properties": { "group_pk": { "type": "string", "format": "byte", "title": "group_pk is the public key of the group" }, "group_type": { "$ref": "#/definitions/v1GroupType", "title": "group_type is the type of the group" }, "contact_pk": { "type": "string", "format": "byte", "title": "contact_pk is the contact public key if appropriate" } } }, "v1DecodeContactReply": { "type": "object", "properties": { "contact": { "$ref": "#/definitions/v1ShareableContact", "description": "shareable_contact is the decoded shareable contact." } } }, "v1Direction": { "type": "string", "enum": [ "UnknownDir", "InboundDir", "OutboundDir", "BiDir" ], "default": "UnknownDir" }, "v1EventContext": { "type": "object", "properties": { "id": { "type": "string", "format": "byte", "title": "id is the CID of the underlying OrbitDB event" }, "parent_ids": { "type": "array", "items": { "type": "string", "format": "byte" }, "title": "id are the the CIDs of the underlying parents of the OrbitDB event" }, "group_pk": { "type": "string", "format": "byte", "title": "group_pk receiving the event" } }, "title": "EventContext adds context (its id, its parents and its attachments) to an event" }, "v1EventType": { "type": "string", "enum": [ "EventTypeUndefined", "EventTypeGroupMemberDeviceAdded", "EventTypeGroupDeviceChainKeyAdded", "EventTypeAccountGroupJoined", "EventTypeAccountGroupLeft", "EventTypeAccountContactRequestDisabled", "EventTypeAccountContactRequestEnabled", "EventTypeAccountContactRequestReferenceReset", "EventTypeAccountContactRequestOutgoingEnqueued", "EventTypeAccountContactRequestOutgoingSent", "EventTypeAccountContactRequestIncomingReceived", "EventTypeAccountContactRequestIncomingDiscarded", "EventTypeAccountContactRequestIncomingAccepted", "EventTypeAccountContactBlocked", "EventTypeAccountContactUnblocked", "EventTypeContactAliasKeyAdded", "EventTypeMultiMemberGroupAliasResolverAdded", "EventTypeMultiMemberGroupInitialMemberAnnounced", "EventTypeMultiMemberGroupAdminRoleGranted", "EventTypeGroupReplicating", "EventTypeAccountVerifiedCredentialRegistered", "EventTypeGroupMetadataPayloadSent" ], "default": "EventTypeUndefined", "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" }, "v1Group": { "type": "object", "properties": { "public_key": { "type": "string", "format": "byte", "title": "public_key is the identifier of the group, it signs the group secret and the initial member of a multi-member group" }, "secret": { "type": "string", "format": "byte", "title": "secret is the symmetric secret of the group, which is used to encrypt the metadata" }, "secret_sig": { "type": "string", "format": "byte", "title": "secret_sig is the signature of the secret used to ensure the validity of the group" }, "group_type": { "$ref": "#/definitions/v1GroupType", "title": "group_type specifies the type of the group, used to determine how device chain key is generated" }, "sign_pub": { "type": "string", "format": "byte", "title": "sign_pub is the signature public key used to verify entries, not required when secret and secret_sig are provided" }, "link_key": { "type": "string", "format": "byte", "title": "link_key is the secret key used to exchange group updates and links to attachments, useful for replication services" }, "link_key_sig": { "type": "string", "format": "byte", "title": "link_key_sig is the signature of the link_key using the group private key" } }, "title": "Group define a group and is enough to invite someone to it" }, "v1GroupDeviceStatusReply": { "type": "object", "properties": { "type": { "$ref": "#/definitions/GroupDeviceStatusType" }, "event": { "type": "string", "format": "byte" } } }, "v1GroupInfoReply": { "type": "object", "properties": { "group": { "$ref": "#/definitions/v1Group", "title": "group is the group invitation, containing the group pk and its type" }, "member_pk": { "type": "string", "format": "byte", "title": "member_pk is the identifier of the current member in the group" }, "device_pk": { "type": "string", "format": "byte", "title": "device_pk is the identifier of the current device in the group" } } }, "v1GroupMessageEvent": { "type": "object", "properties": { "event_context": { "$ref": "#/definitions/v1EventContext", "title": "event_context contains context information about the event" }, "headers": { "$ref": "#/definitions/v1MessageHeaders", "title": "headers contains headers of the secure message" }, "message": { "type": "string", "format": "byte", "title": "message contains the secure message payload" } } }, "v1GroupMetadata": { "type": "object", "properties": { "event_type": { "$ref": "#/definitions/v1EventType", "title": "event_type defines which event type is used" }, "payload": { "type": "string", "format": "byte", "title": "the serialization depends on event_type, event is symmetrically encrypted" }, "sig": { "type": "string", "format": "byte", "title": "sig is the signature of the payload, it depends on the event_type for the used key" }, "protocol_metadata": { "$ref": "#/definitions/v1ProtocolMetadata", "title": "protocol_metadata is protocol layer data" } }, "title": "GroupMetadata is used in GroupEnvelope and only readable by invited group members" }, "v1GroupMetadataEvent": { "type": "object", "properties": { "event_context": { "$ref": "#/definitions/v1EventContext", "title": "event_context contains context information about the event" }, "metadata": { "$ref": "#/definitions/v1GroupMetadata", "title": "metadata contains the newly available metadata" }, "event": { "type": "string", "format": "byte", "title": "event_clear clear bytes for the event" } } }, "v1GroupType": { "type": "string", "enum": [ "GroupTypeUndefined", "GroupTypeAccount", "GroupTypeContact", "GroupTypeMultiMember" ], "default": "GroupTypeUndefined", "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." }, "v1MessageHeaders": { "type": "object", "properties": { "counter": { "type": "string", "format": "uint64", "title": "counter is the current counter value for the specified device" }, "device_pk": { "type": "string", "format": "byte", "title": "device_pk is the public key of the device sending the message" }, "sig": { "type": "string", "format": "byte", "title": "sig is the signature of the encrypted message using the device's private key" }, "metadata": { "type": "object", "additionalProperties": { "type": "string" }, "title": "metadata allow to pass custom informations" } }, "title": "MessageHeaders is used in MessageEnvelope and only readable by invited group members" }, "v1MultiMemberGroupAdminRoleGrantReply": { "type": "object" }, "v1MultiMemberGroupAliasResolverDiscloseReply": { "type": "object" }, "v1MultiMemberGroupCreateReply": { "type": "object", "properties": { "group_pk": { "type": "string", "format": "byte", "title": "group_pk is the identifier of the newly created group" } } }, "v1MultiMemberGroupInvitationCreateReply": { "type": "object", "properties": { "group": { "$ref": "#/definitions/v1Group", "title": "group is the invitation to the group" } } }, "v1MultiMemberGroupJoinReply": { "type": "object" }, "v1MultiMemberGroupLeaveReply": { "type": "object" }, "v1OutOfStoreMessage": { "type": "object", "properties": { "cid": { "type": "string", "format": "byte" }, "device_pk": { "type": "string", "format": "byte" }, "counter": { "type": "string", "format": "uint64" }, "sig": { "type": "string", "format": "byte" }, "flags": { "type": "integer", "format": "int64" }, "encrypted_payload": { "type": "string", "format": "byte" }, "nonce": { "type": "string", "format": "byte" } } }, "v1OutOfStoreReceiveReply": { "type": "object", "properties": { "message": { "$ref": "#/definitions/v1OutOfStoreMessage" }, "cleartext": { "type": "string", "format": "byte" }, "group_public_key": { "type": "string", "format": "byte" }, "already_received": { "type": "boolean" } } }, "v1OutOfStoreSealReply": { "type": "object", "properties": { "encrypted": { "type": "string", "format": "byte" } } }, "v1PeerListPeer": { "type": "object", "properties": { "id": { "type": "string", "description": "id is the libp2p.PeerID." }, "routes": { "type": "array", "items": { "$ref": "#/definitions/PeerListRoute" }, "description": "routes are the list of active and known maddr." }, "errors": { "type": "array", "items": { "type": "string" }, "description": "errors is a list of errors related to the peer." }, "features": { "type": "array", "items": { "$ref": "#/definitions/PeerListFeature" }, "description": "Features is a list of available features." }, "min_latency": { "type": "string", "format": "int64", "description": "MinLatency is the minimum latency across all the peer routes." }, "is_active": { "type": "boolean", "description": "IsActive is true if at least one of the route is active." }, "direction": { "$ref": "#/definitions/v1Direction", "description": "Direction is the aggregate of all the routes's direction." } } }, "v1PeerListReply": { "type": "object", "properties": { "peers": { "type": "array", "items": { "$ref": "#/definitions/v1PeerListPeer" } } } }, "v1ProtocolMetadata": { "type": "object" }, "v1RefreshContactRequestPeer": { "type": "object", "properties": { "id": { "type": "string", "description": "id is the libp2p.PeerID." }, "addrs": { "type": "array", "items": { "type": "string" }, "description": "list of peers multiaddrs." } } }, "v1RefreshContactRequestReply": { "type": "object", "properties": { "peers_found": { "type": "array", "items": { "$ref": "#/definitions/v1RefreshContactRequestPeer" }, "description": "peers found and successfully connected." } } }, "v1ReplicationServiceRegisterGroupReply": { "type": "object" }, "v1ServiceExportDataReply": { "type": "object", "properties": { "exported_data": { "type": "string", "format": "byte" } } }, "v1ServiceGetConfigurationReply": { "type": "object", "properties": { "account_pk": { "type": "string", "format": "byte", "title": "account_pk is the public key of the current account" }, "device_pk": { "type": "string", "format": "byte", "title": "device_pk is the public key of the current device" }, "account_group_pk": { "type": "string", "format": "byte", "title": "account_group_pk is the public key of the account group" }, "peer_id": { "type": "string", "title": "peer_id is the peer ID of the current IPFS node" }, "listeners": { "type": "array", "items": { "type": "string" }, "title": "listeners is the list of swarm listening addresses of the current IPFS node" }, "ble_enabled": { "$ref": "#/definitions/ServiceGetConfigurationSettingState" }, "wifi_p2p_enabled": { "$ref": "#/definitions/ServiceGetConfigurationSettingState" }, "mdns_enabled": { "$ref": "#/definitions/ServiceGetConfigurationSettingState" }, "relay_enabled": { "$ref": "#/definitions/ServiceGetConfigurationSettingState" } } }, "v1ShareContactReply": { "type": "object", "properties": { "encoded_contact": { "type": "string", "format": "byte", "description": "encoded_contact is the Protobuf encoding of the ShareableContact. You can further encode the bytes for sharing, such as base58 or QR code." } } }, "v1ShareableContact": { "type": "object", "properties": { "pk": { "type": "string", "format": "byte", "title": "pk is the account to send a contact request to" }, "public_rendezvous_seed": { "type": "string", "format": "byte", "title": "public_rendezvous_seed is the rendezvous seed used by the account to send a contact request to" }, "metadata": { "type": "string", "format": "byte", "title": "metadata is the metadata specific to the app to identify the contact for the request" } } }, "v1SystemInfoReply": { "type": "object", "properties": { "process": { "$ref": "#/definitions/SystemInfoProcess" }, "p2p": { "$ref": "#/definitions/SystemInfoP2P" }, "orbitdb": { "$ref": "#/definitions/SystemInfoOrbitDB" }, "warns": { "type": "array", "items": { "type": "string" } } } }, "v1VerifiedCredentialsListReply": { "type": "object", "properties": { "credential": { "$ref": "#/definitions/v1AccountVerifiedCredentialRegistered" } } } } } ================================================ FILE: docs/architecture/2020-11-27-adr-berty-grpc-bridge.txt ================================================ ┌──────────────────────────────────────────────────────────────────────────────────────────┐ │service BridgeService { │ │ // CreateClient client a new bridge client │ │ rpc CreateClient (CreateClient.Request) returns (CreateClient.Reply); │ │ │ │ // ClientInvokeUnary invoke a unary method │ │ rpc ClientInvokeUnary (ClientInvokeUnary.Request) returns (ClientInvokeUnary.Reply); │ │ │ │ // CreateStream create a stream │ │ rpc CreateClientStream (ClientCreateStream.Request) returns (ClientCreateStream.Reply); │ │ │ │ // Send Message over the given stream │ │ rpc ClientStreamSend (ClientStreamSend.Request) returns (ClientStreamSend.Reply); │ │ │ │ // Recv message over the given stream │ │ rpc ClientStreamRecv (ClientStreamRecv.Request) returns (ClientStreamRecv.Reply); │ │ │ │ // Close the given stream │ │ rpc ClientStreamClose (ClientStreamClose.Request) returns (ClientStreamClose.Reply); │ │} │ └──────────────────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ┌──────────────────────┘ │ │ ┏━━━━━━┳───────────────┼────────────────────────────────────────────┐ ┏━━━━━━━━━━━━━━━━━━━━━━━━┳─────────────────────┐ ┏━━━━━━┳──────────────────────────────────────────────────────────────┐ ┃ JS ┃ │ │ ┃ NATIVE (ios/android) ┃ │ ┃ Go ┃ │ ┣━━━━━━┛ ◎ │ ┣━━━━━━━━━━━━━━━━━━━━━━━━┛ │ ┣━━━━━━┛ │ │ ┌──────────────────┐ ┌──────────────┐ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ┌────────────┐ ┌────────────────┐ ┌──────────────────┐ │ │ │ │ │ RPC native │ │ │ ┌───────────────────────────────────────────┐│ │ │ │ │ │ │ │ │ │ │ Bridge Service │ │ Transport │ │ │ │ ││ │ │ Buffer │ │ ClientConn │ │ Bridge Service │ │ │ ┌─▶│ Client │────────▶│ (Unary Only) │──────────┼───────┼▶│InvokeMethod (Base64EncodedMessage: String)│├───────────┼───▶│ Listener │────▶│(Bridge Service)│────▶│ Server │──┐│ │ │ │ │ │ │ │ │ │ ││ │ │ │ │ │ │ │ ││ │ │ │ │ │ │ │ │ └───────────────────────────────────────────┘│ │ └────────────┘ └────────────────┘ └──────────────────┘ ││ │ │ │ │ │ │ │ │ │ │ ││ │ │ │ │ │ │ │ │ │ │ ││ │ │ └──────────────────┘ └──────────────┘ │ │ │ │ ┌────────────────────────────────────────────────────────────┘│ │ │ │ │ │ │ │ │ │ └─────────────────────────────────────────────────┐ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ┌──────────────┐ │ │ │ │ │ │ ┌────────────────┐ │ │ │ │ │ │ └──────────────────────────────────────────────┘ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ┌──────────────────────┐ │ │ │ │ │ │ │ │ ┌──────────────────────┐ │ │ │ Messenger Service │ │ │ │ │ │ │ │ │ │ Messenger Service │ │ │ │ Client │────┐ │ │ │ │ │ │ │ │ ┌──────▶│ Server │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └──────────────────────┘ │ │ │ │ │ │ │ │ │ │ └──────────────────────┘ │ │ ┌──────────────────────┐ │ │ Bridge │ │ │ │ │ │ │ │ ┌──────────────────────┐ │ │ │ Protocol Service │ │ │ Transport │ │ │ │ │ │GRPC ClientConn │ │ │ Protocol Service │ │ │ │ Client │────┼───▶│(Stream/Unary)│──┘ │ │ └─▶│(Mixed Services)│───────┼──────▶│ Server │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └──────────────────────┘ │ │ │ │ │ │ │ │ └──────────────────────┘ │ │ ┌──────────────────────┐ │ │ │ │ │ │ │ │ ┌──────────────────────┐ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ... │────┘ │ │ │ │ │ │ └──────▶│ ... │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └──────────────────────┘ │ │ │ │ │ │ └──────────────────────┘ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └──────────────┘ │ │ └────────────────┘ │ │ │ │ │ │ │ │ │ │ │ │ │ └───────────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────────────┘ ================================================ FILE: docs/architecture/2020-11-27-adr-gomobile-ipfs.md ================================================ # GomobileIPFS ## concept ### Driver ``` ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │ │ │ │ │ │ │ Native │ │ Init Driver │ │ Go │ │ (IOS/Android) │────────────▶│ │───────────▶│ │ │ │ │ │ │ │ └───────────────┘ └───────────────┘ └───────────────┘ │ │ │ │ │ │ │ ▼ │ ┌───────────────┐ │ │ │ │ implement │ Interface │ └─────────────────────────────────────────────────▶│ (Driver) │ │ │ └───────────────┘ ``` - Go can call Native by calling **Driver** method directly - Native can call Go Method with **Driver** `RegisterHandler` method #### RegisterHandler example _example from berty_ ```go // BackgroundTask type BackgroundTaskHandlerDriver interface { RegisterHandler(BackgroundTaskHandler) } type BackgroundTaskHandler interface { HandleTask() LifeCycleBackgroundTask } type BackgroundTaskDriver interface { Execute() (success bool) } ``` [berty](https://github.com/berty/berty/blob/master/go/framework/bertynative/driver_lifecycle.go) ##### implement in swift ```swift // BackgroundTaskDriver implement BackgroundTaskHandler public class BackgroundTaskDriver: BackgroundTaskHandlerDriverProtocol { let handler: BackgroundTaskHandlerProtocol func registerHandler(handler: BackgroundTaskHandlerProtocol) { self.handler = handler } func executeGoBackgroundTask() { let success = self.handler.Execute() } } ``` [berty](https://github.com/berty/berty/blob/master/js/ios/Berty/Sources/LifeCycle.swift#L24) ##### Usage in gomobileipfs user will then simply register the driver to ios background task system then pass it to go ```swift // pseudo code below // ... backgroundDriver = BackgroundTaskDriver() // user register registerLifecycleBackgroundTaskToIOS("myapp.ios.background-task", backgroundDriver) // go node init let node = IPFSNode() node.withBackgroundTaskDriver(backgroundDriver) // ... ``` [berty](https://github.com/berty/berty/blob/master/js/ios/Berty/AppDelegate.m#L54) ### GomobileIPFS \w berty ``` ╔════════╦━━━━━━━━━━━━━━━━┓ ┏╦═════════════╦━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ║ Berty ║ ┃ ┃║GomobileIPFS ║ ┃ ╠════════╝ ┃ ┃╚═════════════╝ ┃ ┃ ┃ ┃ ┃ ┃ ┌────────────┐ ┃ ┃ ┌────────────┐ ┃ ┃ │ │ ┃ ┃ │ │ ┃ ┃ │ pkg │ ┃ ┃ │ pkg │ ┃ ┃ │ │ ┃ ┃ │ │ ┃ ┃ └────────────┘ ┃ ┃ └────────────┘ ┃ ┃ │ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ┌──────────────────┴────────────────┐ ┃ ┃ ▼ ┃ ┃ ▼ ▼ ┃ ┃ ┌────────────┐ ┃ ┃ ┌────────────┐ ┌────────────┐ ┃ ┃ │ │ ┃ ┃ │ │ │ │ ┃ ┃ │ bridge │◀─────╋─import─╋────────│ driver │────────import─┐ │ node │ ┃ ┃ │ │ ┃ ┃ │ │ │ │ │ ┃ ┃ └────────────┘ ┃ ┃ └────────────┘ │ └────────────┘ ┃ ┃ │ ┃ ┃ ▲ ▲ │ │ ┃ ┃ │ ┃ ┃ │ │ │ └───────┐ ┃ ┃ │ ┃ ┃ │ │ │ ▼ ┃ ┃ │ ┃ ┃ │ │ │ ┌───────────┐ ┃ ┃ │ ┃ ┃ │ │ │ │ │ ┃ ┃ │ ┃ ┃ │ │ └──────────────▶│ bind │ ┃ ┃ │ ┃ ┃ │ │ │ │ ┃ ┃ │ ┃ ┃ │ │ └───────────┘ ┃ ┃ │ ┃ ┃ │ │ │ ┃ ┃ │ ┃ ┃ │ │ │ ┃ ┃ │ ┃ ┃ implement │ ┃ ┃ │ ┃ ┃ │ │ │ ┃ ┃ │ ┃ ┃ │ │ │ ┃ ┃ gobind ┃ ┃ │ │ gobind ┃ ┃ │ ┃ ┃ │ │ │ ┃ ┃ │ ┃ ┃ │ │ │ ┃ ┃ │ ┃ ┃ │ │ │ ┃ ┃ │ ┃ ┃ │ │ │ ┃ ┃ │ ┃ ┃ │ │ │ ┃ ┃ ▼ ┃ ┃ │ │ ▼ ┃ ┃ ┏━━━━━━━━━━━━┓ ┃ ┃ │ │ ┏━━━━━━━━━━━━┓ ┃ ┃ ┃ Berty ┃ ┃ ┃ │ │ ┃GomobileIPFS┃ ┃ ┃ ┃ Framework ┃──────╋────────╋────────────┘ └─────────────────────────────────┃ Framework ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┗━━━━━━━━━━━━┛ ┃ ┃ ┗━━━━━━━━━━━━┛ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ``` ================================================ FILE: docs/architecture/README.md ================================================ # Architecture This 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. ================================================ FILE: docs/architecture/messenger-mvp/README.md ================================================ # Berty Messenger MVP ## Reading linked graphs ### Account group/log back and forth ignored Sending 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 ## Contact request ![Graph](contact-request.mermaid.svg) ================================================ FILE: docs/architecture/messenger-mvp/contact-request.mermaid ================================================ sequenceDiagram participant aorbitdb as Alice orbitdb participant aprotocol as Alice fake protocol participant achat as Alice chat app participant a as Alice participant b as Bob participant bchat as Bob chat app participant bprotocol as Bob fake protocol participant borbitdb as Bob orbitdb a->>achat: Start app achat->>aprotocol: Call contactRequestEnable command aprotocol-->>aorbitdb: Subscribe to Alice rdv log aprotocol->>achat: Send contactRequestEnabled event with reference achat->>a: Display QR code with reference and Alice metadata a->>b: Show/Send QR with Alice reference and contact metadata b->>bchat: Scan QR with Alice reference and contact metadata bchat-->> bprotocol: Subscribe to 1to1 group metadata bprotocol-->> borbitdb: Subscribe to 1to1 group metadata log bchat->>bprotocol: Call sendContactRequest command bprotocol-->> bchat: Send contactRequestEnqueued event bchat-->> b: Display an outgoing contact request bprotocol->>borbitdb: Send Bob fake public key, 1to1 group fake public key and contact metadata on Alice rdv log borbitdb->>aorbitdb: Alice rdv log replication aorbitdb->>aprotocol: Send contact request with bob data from rdv log aprotocol->>achat: Send contactRequestReceived event achat->> a: Display contact request a->> achat: Touch accept contact request achat->> aprotocol: Call contactRequestAccept command aprotocol->> aorbitdb: Send groupMemberDeviceAdded event on the 1to1 group metadata log aorbitdb->>borbitdb: 1to1 group metadata log replication borbitdb->> bprotocol: Send groupMemberDeviceAdded event from the 1to1 group metadata log bprotocol->> bchat: Send groupMemberDeviceAdded event from the 1to1 group metadata bchat->> b: Show that the request is accepted ================================================ FILE: docs/buf-doc.gen.yaml ================================================ version: v1 plugins: - plugin: doc out: ./ opt: markdown,api.md.tmp - plugin: swagger out: ./ opt: - logtostderr=true ================================================ FILE: docs/gen.sum ================================================ 10db4498e1b002bb5bbfeb67ec3ce4d259b35c3d Makefile ================================================ FILE: docs/ideas/distributed-entropy.md ================================================ # Distributed entropy Date: 2019-05-24 Author: Antoine Eddi (aeddi) ## Description The idea would be to exchange random data blocks between trusted devices (owned by the same account or by different contacts). The 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. All 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. NB: 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. ## Why? A 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. ## To be considered We don't know enough about the subject to assess whether it's a bad practice to do this kind of thing. ================================================ FILE: docs/ideas/entropy-pool.md ================================================ # Entropy pool Date: 2019-05-24 Author: Manfred Touron (moul) ## Description The 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. Random data added to the pool could be reused later to generate new cryptographic materials. We could ensure that the pool is built when the device is idle and its battery is fully charged. For example, in the case of a phone: when it charges during the night. ## Why? An attacker could lower the entropy level at some point and make the numbers generated predictable. ## To be considered We don't know enough about the subject to assess whether it's a bad practice to do this kind of thing. ================================================ FILE: docs/protocol/README.md ================================================ # Wesh protocol The Wesh protocol is documented at https://berty.tech/docs/protocol . ================================================ FILE: events.go ================================================ package weshnet import ( "fmt" cid "github.com/ipfs/go-cid" "golang.org/x/crypto/nacl/secretbox" "google.golang.org/protobuf/proto" ipfslog "berty.tech/go-ipfs-log" "berty.tech/weshnet/v2/pkg/cryptoutil" "berty.tech/weshnet/v2/pkg/errcode" "berty.tech/weshnet/v2/pkg/protocoltypes" ) var eventTypesMapper = map[protocoltypes.EventType]struct { Message proto.Message SigChecker sigChecker }{ protocoltypes.EventType_EventTypeGroupMemberDeviceAdded: {Message: &protocoltypes.GroupMemberDeviceAdded{}, SigChecker: sigCheckerGroupMemberDeviceAdded}, protocoltypes.EventType_EventTypeGroupDeviceChainKeyAdded: {Message: &protocoltypes.GroupDeviceChainKeyAdded{}, SigChecker: sigCheckerDeviceSigned}, protocoltypes.EventType_EventTypeAccountGroupJoined: {Message: &protocoltypes.AccountGroupJoined{}, SigChecker: sigCheckerDeviceSigned}, protocoltypes.EventType_EventTypeAccountGroupLeft: {Message: &protocoltypes.AccountGroupLeft{}, SigChecker: sigCheckerDeviceSigned}, protocoltypes.EventType_EventTypeAccountContactRequestDisabled: {Message: &protocoltypes.AccountContactRequestDisabled{}, SigChecker: sigCheckerDeviceSigned}, protocoltypes.EventType_EventTypeAccountContactRequestEnabled: {Message: &protocoltypes.AccountContactRequestEnabled{}, SigChecker: sigCheckerDeviceSigned}, protocoltypes.EventType_EventTypeAccountContactRequestReferenceReset: {Message: &protocoltypes.AccountContactRequestReferenceReset{}, SigChecker: sigCheckerDeviceSigned}, protocoltypes.EventType_EventTypeAccountContactRequestOutgoingEnqueued: {Message: &protocoltypes.AccountContactRequestOutgoingEnqueued{}, SigChecker: sigCheckerDeviceSigned}, protocoltypes.EventType_EventTypeAccountContactRequestOutgoingSent: {Message: &protocoltypes.AccountContactRequestOutgoingSent{}, SigChecker: sigCheckerDeviceSigned}, protocoltypes.EventType_EventTypeAccountContactRequestIncomingReceived: {Message: &protocoltypes.AccountContactRequestIncomingReceived{}, SigChecker: sigCheckerDeviceSigned}, protocoltypes.EventType_EventTypeAccountContactRequestIncomingDiscarded: {Message: &protocoltypes.AccountContactRequestIncomingDiscarded{}, SigChecker: sigCheckerDeviceSigned}, protocoltypes.EventType_EventTypeAccountContactRequestIncomingAccepted: {Message: &protocoltypes.AccountContactRequestIncomingAccepted{}, SigChecker: sigCheckerDeviceSigned}, protocoltypes.EventType_EventTypeAccountContactBlocked: {Message: &protocoltypes.AccountContactBlocked{}, SigChecker: sigCheckerDeviceSigned}, protocoltypes.EventType_EventTypeAccountContactUnblocked: {Message: &protocoltypes.AccountContactUnblocked{}, SigChecker: sigCheckerDeviceSigned}, protocoltypes.EventType_EventTypeContactAliasKeyAdded: {Message: &protocoltypes.ContactAliasKeyAdded{}, SigChecker: sigCheckerDeviceSigned}, protocoltypes.EventType_EventTypeMultiMemberGroupAliasResolverAdded: {Message: &protocoltypes.MultiMemberGroupAliasResolverAdded{}, SigChecker: sigCheckerDeviceSigned}, protocoltypes.EventType_EventTypeMultiMemberGroupInitialMemberAnnounced: {Message: &protocoltypes.MultiMemberGroupInitialMemberAnnounced{}, SigChecker: sigCheckerGroupSigned}, protocoltypes.EventType_EventTypeMultiMemberGroupAdminRoleGranted: {Message: &protocoltypes.MultiMemberGroupAdminRoleGranted{}, SigChecker: sigCheckerDeviceSigned}, protocoltypes.EventType_EventTypeGroupMetadataPayloadSent: {Message: &protocoltypes.GroupMetadataPayloadSent{}, SigChecker: sigCheckerDeviceSigned}, protocoltypes.EventType_EventTypeGroupReplicating: {Message: &protocoltypes.GroupReplicating{}, SigChecker: sigCheckerDeviceSigned}, protocoltypes.EventType_EventTypeAccountVerifiedCredentialRegistered: {Message: &protocoltypes.AccountVerifiedCredentialRegistered{}, SigChecker: sigCheckerDeviceSigned}, } func newEventContext(eventID cid.Cid, parentIDs []cid.Cid, g *protocoltypes.Group) *protocoltypes.EventContext { parentIDsBytes := make([][]byte, len(parentIDs)) for i, parentID := range parentIDs { parentIDsBytes[i] = parentID.Bytes() } return &protocoltypes.EventContext{ Id: eventID.Bytes(), ParentIds: parentIDsBytes, GroupPk: g.PublicKey, } } // FIXME(gfanton): getParentsCID use a lot of resources // nolint:unused func getParentsForCID(log ipfslog.Log, c cid.Cid) []cid.Cid { if log == nil { // TODO: this should not happen return []cid.Cid{} } parent, ok := log.Get(c) // Can't fetch parent entry if !ok { return []cid.Cid{} } nextEntries := parent.GetNext() // Parent has only one or no parents, returning its id if len(nextEntries) <= 1 { return []cid.Cid{parent.GetHash()} } // Parent has more than one parent, returning parent entries var ret []cid.Cid for _, n := range nextEntries { ret = append(ret, getParentsForCID(log, n)...) } return ret } func newGroupMetadataEventFromEntry(_ ipfslog.Log, e ipfslog.Entry, metadata *protocoltypes.GroupMetadata, event proto.Message, g *protocoltypes.Group) (*protocoltypes.GroupMetadataEvent, error) { // TODO: if parent is a merge node we should return the next nodes of it eventBytes, err := proto.Marshal(event) if err != nil { return nil, errcode.ErrCode_ErrSerialization } // TODO(gfanton): getParentsCID use a lot of resources, disable it until we need it // evtCtx := newEventContext(e.GetHash(), getParentsForCID(log, e.GetHash()), group, attachmentsCIDs) evtCtx := newEventContext(e.GetHash(), []cid.Cid{}, g) gme := protocoltypes.GroupMetadataEvent{ EventContext: evtCtx, Metadata: metadata, Event: eventBytes, } return &gme, nil } func openGroupEnvelope(g *protocoltypes.Group, envelopeBytes []byte) (*protocoltypes.GroupMetadata, proto.Message, error) { env := &protocoltypes.GroupEnvelope{} if err := proto.Unmarshal(envelopeBytes, env); err != nil { return nil, nil, errcode.ErrCode_ErrInvalidInput.Wrap(err) } nonce, err := cryptoutil.NonceSliceToArray(env.Nonce) if err != nil { return nil, nil, errcode.ErrCode_ErrSerialization.Wrap(err) } data, ok := secretbox.Open(nil, env.Event, nonce, g.GetSharedSecret()) if !ok { return nil, nil, errcode.ErrCode_ErrGroupMemberLogEventOpen } metadataEvent := &protocoltypes.GroupMetadata{} err = proto.Unmarshal(data, metadataEvent) if err != nil { return nil, nil, errcode.ErrCode_TODO.Wrap(err) } et, ok := eventTypesMapper[metadataEvent.EventType] if !ok { return nil, nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("event type not found")) } payload := proto.Clone(et.Message) if err := proto.Unmarshal(metadataEvent.Payload, payload); err != nil { return nil, nil, errcode.ErrCode_ErrDeserialization.Wrap(err) } if err := et.SigChecker(g, metadataEvent, payload); err != nil { return nil, nil, errcode.ErrCode_ErrCryptoSignatureVerification.Wrap(err) } return metadataEvent, payload, nil } func sealGroupEnvelope(g *protocoltypes.Group, eventType protocoltypes.EventType, payload proto.Message, payloadSig []byte) ([]byte, error) { payloadBytes, err := proto.Marshal(payload) if err != nil { return nil, errcode.ErrCode_TODO.Wrap(err) } nonce, err := cryptoutil.GenerateNonce() if err != nil { return nil, errcode.ErrCode_ErrCryptoNonceGeneration.Wrap(err) } event := &protocoltypes.GroupMetadata{ EventType: eventType, Payload: payloadBytes, Sig: payloadSig, ProtocolMetadata: &protocoltypes.ProtocolMetadata{}, } eventClearBytes, err := proto.Marshal(event) if err != nil { return nil, errcode.ErrCode_ErrSerialization.Wrap(err) } eventBytes := secretbox.Seal(nil, eventClearBytes, nonce, g.GetSharedSecret()) env := &protocoltypes.GroupEnvelope{ Event: eventBytes, Nonce: nonce[:], } return proto.Marshal(env) } ================================================ FILE: events_sig_checkers.go ================================================ package weshnet import ( "github.com/libp2p/go-libp2p/core/crypto" "google.golang.org/protobuf/proto" "berty.tech/weshnet/v2/pkg/errcode" "berty.tech/weshnet/v2/pkg/protocoltypes" ) type sigChecker func(g *protocoltypes.Group, metadata *protocoltypes.GroupMetadata, message proto.Message) error func sigCheckerGroupSigned(g *protocoltypes.Group, metadata *protocoltypes.GroupMetadata, _ proto.Message) error { pk, err := g.GetPubKey() if err != nil { return err } ok, err := pk.Verify(metadata.Payload, metadata.Sig) if err != nil { return errcode.ErrCode_ErrCryptoSignatureVerification.Wrap(err) } if !ok { return errcode.ErrCode_ErrCryptoSignatureVerification } return nil } type eventDeviceSigned interface { proto.Message GetDevicePk() []byte } func sigCheckerDeviceSigned(_ *protocoltypes.Group, metadata *protocoltypes.GroupMetadata, message proto.Message) error { msg, ok := message.(eventDeviceSigned) if !ok { return errcode.ErrCode_ErrDeserialization } devPK, err := crypto.UnmarshalEd25519PublicKey(msg.GetDevicePk()) if err != nil { return errcode.ErrCode_ErrDeserialization.Wrap(err) } ok, err = devPK.Verify(metadata.Payload, metadata.Sig) if err != nil { return errcode.ErrCode_ErrCryptoSignatureVerification.Wrap(err) } if !ok { return errcode.ErrCode_ErrCryptoSignatureVerification } return nil } func sigCheckerGroupMemberDeviceAdded(g *protocoltypes.Group, metadata *protocoltypes.GroupMetadata, message proto.Message) error { msg, ok := message.(*protocoltypes.GroupMemberDeviceAdded) if !ok { return errcode.ErrCode_ErrDeserialization } memPK, err := crypto.UnmarshalEd25519PublicKey(msg.MemberPk) if err != nil { return errcode.ErrCode_ErrDeserialization.Wrap(err) } ok, err = memPK.Verify(msg.DevicePk, msg.MemberSig) if err != nil { return errcode.ErrCode_ErrCryptoSignatureVerification.Wrap(err) } if !ok { return errcode.ErrCode_ErrCryptoSignatureVerification } return sigCheckerDeviceSigned(g, metadata, message) } ================================================ FILE: gen.sum ================================================ 78cfa180bfe8caf1572068cd6ea6bee4b89b3348 Makefile ================================================ FILE: go.mod ================================================ module berty.tech/weshnet/v2 go 1.22 toolchain go1.22.5 require ( berty.tech/go-ipfs-log v1.10.3-0.20240719141234-29e2d26e2aeb berty.tech/go-ipfs-repo-encrypted v1.3.1-0.20240722095251-c6b363b38785 berty.tech/go-orbit-db v1.22.2-0.20240719144258-ec7d1faaca68 filippo.io/edwards25519 v1.0.0 github.com/aead/ecdh v0.2.0 github.com/berty/emitter-go v0.0.0-20221031144724-5dae963c3622 github.com/berty/go-libp2p-rendezvous v0.5.1 github.com/buicongtan1997/protoc-gen-swagger-config v0.0.0-20200705084907-1342b78c1a7e github.com/daixiang0/gci v0.8.2 github.com/dgraph-io/badger/v2 v2.2007.3 github.com/gofrs/uuid v4.3.1+incompatible github.com/golang/protobuf v1.5.4 github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 github.com/grpc-ecosystem/grpc-gateway v1.16.0 github.com/hyperledger/aries-framework-go v0.1.9-0.20221202141134-083803ecf0a3 github.com/ipfs/go-cid v0.4.1 github.com/ipfs/go-datastore v0.6.0 github.com/ipfs/go-ds-badger2 v0.1.3 github.com/ipfs/go-ipfs-keystore v0.1.0 github.com/ipfs/go-ipld-cbor v0.1.0 github.com/ipfs/go-log/v2 v2.5.1 github.com/ipfs/kubo v0.29.0 github.com/juju/fslock v0.0.0-20160525022230-4d5c94c67b4b github.com/libp2p/go-libp2p v0.34.1 github.com/libp2p/go-libp2p-kad-dht v0.25.2 github.com/libp2p/go-libp2p-pubsub v0.11.1-0.20240711152552-e508d8643ddb github.com/libp2p/go-libp2p-testing v0.12.0 github.com/mdomke/git-semver/v5 v5.0.0 github.com/multiformats/go-multiaddr v0.12.4 github.com/multiformats/go-multiaddr-dns v0.3.1 github.com/multiformats/go-multiaddr-fmt v0.1.0 github.com/multiformats/go-multibase v0.2.0 github.com/multiformats/go-multihash v0.2.3 github.com/piprate/json-gold v0.4.2 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.19.1 github.com/pseudomuto/protoc-gen-doc v1.5.1 github.com/srikrsna/protoc-gen-gotag v1.0.1 github.com/stretchr/testify v1.9.0 go.uber.org/goleak v1.3.0 go.uber.org/multierr v1.11.0 go.uber.org/zap v1.27.0 golang.org/x/crypto v0.24.0 golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 google.golang.org/grpc v1.65.0 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 google.golang.org/grpc/examples v0.0.0-20200922230038-4e932bbcb079 google.golang.org/protobuf v1.34.2 moul.io/openfiles v1.2.0 moul.io/srand v1.6.1 moul.io/testman v1.5.0 moul.io/u v1.27.0 moul.io/zapfilter v1.7.0 moul.io/zapring v1.3.3 mvdan.cc/gofumpt v0.4.0 ) require ( bazil.org/fuse v0.0.0-20200117225306-7b5117fecadc // indirect contrib.go.opencensus.io/exporter/prometheus v0.4.2 // indirect github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect github.com/DataDog/zstd v1.4.1 // indirect github.com/Jorropo/jsync v1.0.1 // indirect github.com/Masterminds/goutils v1.1.0 // indirect github.com/Masterminds/semver v1.5.0 // indirect github.com/Masterminds/sprig v2.22.0+incompatible // indirect github.com/VictoriaMetrics/fastcache v1.5.7 // indirect github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9 // indirect github.com/alexbrainman/goissue34681 v0.0.0-20191006012335-3fc7a47baff5 // indirect github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/btcsuite/btcd v0.22.1 // indirect github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/ceramicnetwork/go-dag-jose v0.1.0 // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cheggaaa/pb v1.0.29 // indirect github.com/containerd/cgroups v1.1.0 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/crackcomm/go-gitignore v0.0.0-20231225121904-e25f5bc08668 // indirect github.com/cskr/pubsub v1.0.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect github.com/dgraph-io/badger v1.6.2 // indirect github.com/dgraph-io/ristretto v0.0.3 // indirect github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/eclipse/paho.mqtt.golang v1.4.2 // indirect github.com/elastic/gosigar v0.14.2 // indirect github.com/elgris/jsondiff v0.0.0-20160530203242-765b5c24c302 // indirect github.com/emicklei/proto v1.6.13 // indirect github.com/envoyproxy/protoc-gen-validate v1.0.4 // indirect github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5 // indirect github.com/fatih/color v1.13.0 // indirect github.com/fatih/structtag v1.2.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/flynn/noise v1.1.0 // indirect github.com/francoispqt/gojay v1.2.13 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect github.com/go-logr/logr v1.4.1 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/glog v1.2.1 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/gopacket v1.1.19 // indirect github.com/google/pprof v0.0.0-20240509144519-723abb6459b7 // indirect github.com/google/tink/go v1.7.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/mux v1.8.1 // indirect github.com/gorilla/websocket v1.5.1 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/golang-lru v1.0.2 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/hexops/gotextdiff v1.0.3 // indirect github.com/huandu/xstrings v1.3.2 // indirect github.com/huin/goupnp v1.3.0 // indirect github.com/hyperledger/aries-framework-go/spi v0.0.0-20221025204933-b807371b6f1e // indirect github.com/hyperledger/ursa-wrapper-go v0.3.1 // indirect github.com/imdario/mergo v0.3.11 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/ipfs-shipyard/nopfs v0.0.12 // indirect github.com/ipfs-shipyard/nopfs/ipfs v0.13.2-0.20231027223058-cde3b5ba964c // indirect github.com/ipfs/bbloom v0.0.4 // indirect github.com/ipfs/boxo v0.20.0 // indirect github.com/ipfs/go-bitfield v1.1.0 // indirect github.com/ipfs/go-block-format v0.2.0 // indirect github.com/ipfs/go-blockservice v0.5.2 // indirect github.com/ipfs/go-cidutil v0.1.0 // indirect github.com/ipfs/go-ds-badger v0.3.0 // indirect github.com/ipfs/go-ds-flatfs v0.5.1 // indirect github.com/ipfs/go-ds-leveldb v0.5.0 // indirect github.com/ipfs/go-ds-measure v0.2.0 // indirect github.com/ipfs/go-ds-sql v0.3.0 // indirect github.com/ipfs/go-fs-lock v0.0.7 // indirect github.com/ipfs/go-ipfs-blockstore v1.3.1 // indirect github.com/ipfs/go-ipfs-cmds v0.11.0 // indirect github.com/ipfs/go-ipfs-delay v0.0.1 // indirect github.com/ipfs/go-ipfs-ds-help v1.1.1 // indirect github.com/ipfs/go-ipfs-exchange-interface v0.2.1 // indirect github.com/ipfs/go-ipfs-pq v0.0.3 // indirect github.com/ipfs/go-ipfs-redirects-file v0.1.1 // indirect github.com/ipfs/go-ipfs-util v0.0.3 // indirect github.com/ipfs/go-ipld-format v0.6.0 // indirect github.com/ipfs/go-ipld-git v0.1.1 // indirect github.com/ipfs/go-ipld-legacy v0.2.1 // indirect github.com/ipfs/go-libipfs v0.6.2 // indirect github.com/ipfs/go-log v1.0.5 // indirect github.com/ipfs/go-merkledag v0.11.0 // indirect github.com/ipfs/go-metrics-interface v0.0.1 // indirect github.com/ipfs/go-peertaskqueue v0.8.1 // indirect github.com/ipfs/go-unixfsnode v1.9.0 // indirect github.com/ipfs/go-verifcid v0.0.3 // indirect github.com/ipld/go-car v0.6.2 // indirect github.com/ipld/go-car/v2 v2.13.1 // indirect github.com/ipld/go-codec-dagpb v1.6.0 // indirect github.com/ipld/go-ipld-prime v0.21.0 // indirect github.com/jackpal/go-nat-pmp v1.0.2 // indirect github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect github.com/jbenet/goprocess v0.1.4 // indirect github.com/kilic/bls12-381 v0.1.1-0.20210503002446-7b7597926c69 // indirect github.com/klauspost/compress v1.17.8 // indirect github.com/klauspost/cpuid/v2 v2.2.7 // indirect github.com/koron/go-ssdp v0.0.4 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/libp2p/go-cidranger v1.1.0 // indirect github.com/libp2p/go-doh-resolver v0.4.0 // indirect github.com/libp2p/go-flow-metrics v0.1.0 // indirect github.com/libp2p/go-libp2p-asn-util v0.4.1 // indirect github.com/libp2p/go-libp2p-gostream v0.6.0 // indirect github.com/libp2p/go-libp2p-http v0.5.0 // indirect github.com/libp2p/go-libp2p-kbucket v0.6.3 // indirect github.com/libp2p/go-libp2p-pubsub-router v0.6.0 // indirect github.com/libp2p/go-libp2p-record v0.2.0 // indirect github.com/libp2p/go-libp2p-routing-helpers v0.7.3 // indirect github.com/libp2p/go-libp2p-xor v0.1.0 // indirect github.com/libp2p/go-msgio v0.3.0 // indirect github.com/libp2p/go-nat v0.2.0 // indirect github.com/libp2p/go-netroute v0.2.1 // indirect github.com/libp2p/go-reuseport v0.4.0 // indirect github.com/libp2p/go-yamux/v4 v4.0.1 // indirect github.com/libp2p/zeroconf/v2 v2.2.0 // indirect github.com/lyft/protoc-gen-star/v2 v2.0.3 // indirect github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect github.com/maruel/circular v0.0.0-20200815005550-36e533b830e9 // indirect github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.8 // indirect github.com/mattn/go-sqlite3 v1.14.16 // indirect github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect github.com/miekg/dns v1.1.59 // indirect github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect github.com/minio/sha256-simd v1.0.1 // indirect github.com/mitchellh/copystructure v1.0.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/reflectwalk v1.0.1 // indirect github.com/mr-tron/base58 v1.2.0 // indirect github.com/multiformats/go-base32 v0.1.0 // indirect github.com/multiformats/go-base36 v0.2.0 // indirect github.com/multiformats/go-multicodec v0.9.0 // indirect github.com/multiformats/go-multistream v0.5.0 // indirect github.com/multiformats/go-varint v0.0.7 // indirect github.com/mutecomm/go-sqlcipher/v4 v4.4.2 // indirect github.com/mwitkow/go-proto-validators v0.0.0-20180403085117-0950a7990007 // indirect github.com/onsi/ginkgo/v2 v2.17.3 // indirect github.com/opencontainers/runtime-spec v1.2.0 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/openzipkin/zipkin-go v0.4.3 // indirect github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9 // indirect github.com/peterbourgon/ff/v3 v3.0.0 // indirect github.com/pion/datachannel v1.5.6 // indirect github.com/pion/dtls/v2 v2.2.11 // indirect github.com/pion/ice/v2 v2.3.24 // indirect github.com/pion/interceptor v0.1.29 // indirect github.com/pion/logging v0.2.2 // indirect github.com/pion/mdns v0.0.12 // indirect github.com/pion/randutil v0.1.0 // indirect github.com/pion/rtcp v1.2.14 // indirect github.com/pion/rtp v1.8.6 // indirect github.com/pion/sctp v1.8.16 // indirect github.com/pion/sdp/v3 v3.0.9 // indirect github.com/pion/srtp/v2 v2.0.18 // indirect github.com/pion/stun v0.6.1 // indirect github.com/pion/transport/v2 v2.2.5 // indirect github.com/pion/turn/v2 v2.1.6 // indirect github.com/pion/webrtc/v3 v3.2.40 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/polydawn/refmt v0.89.0 // indirect github.com/pquerna/cachecontrol v0.1.0 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.53.0 // indirect github.com/prometheus/procfs v0.15.0 // indirect github.com/prometheus/statsd_exporter v0.22.7 // indirect github.com/pseudomuto/protokit v0.2.0 // indirect github.com/quic-go/qpack v0.4.0 // indirect github.com/quic-go/quic-go v0.44.0 // indirect github.com/quic-go/webtransport-go v0.8.0 // indirect github.com/raulk/go-watchdog v1.3.0 // indirect github.com/rs/cors v1.10.1 // indirect github.com/samber/lo v1.39.0 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/spf13/afero v1.10.0 // indirect github.com/spf13/cobra v1.6.1 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/square/go-jose/v3 v3.0.0-20200630053402-0a67ce9b0693 // indirect github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect github.com/teserakt-io/golang-ed25519 v0.0.0-20210104091850-3888c087a4c8 // indirect github.com/ucarion/urlpath v0.0.0-20200424170820-7ccc79b76bbb // indirect github.com/whyrusleeping/base32 v0.0.0-20170828182744-c30ac30633cc // indirect github.com/whyrusleeping/cbor v0.0.0-20171005072247-63513f603b11 // indirect github.com/whyrusleeping/cbor-gen v0.1.1 // indirect github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f // indirect github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 // indirect github.com/whyrusleeping/go-sysinfo v0.0.0-20190219211824-4a357d4b90b1 // indirect github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0 // indirect go.opentelemetry.io/otel v1.26.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.26.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.26.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.26.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.26.0 // indirect go.opentelemetry.io/otel/exporters/zipkin v1.26.0 // indirect go.opentelemetry.io/otel/metric v1.26.0 // indirect go.opentelemetry.io/otel/sdk v1.26.0 // indirect go.opentelemetry.io/otel/trace v1.26.0 // indirect go.opentelemetry.io/proto/otlp v1.2.0 // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/dig v1.17.1 // indirect go.uber.org/fx v1.21.1 // indirect go.uber.org/mock v0.4.0 // indirect go4.org v0.0.0-20230225012048-214862532bf5 // indirect golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect golang.org/x/mod v0.17.0 // indirect golang.org/x/net v0.26.0 // indirect golang.org/x/oauth2 v0.20.0 // indirect golang.org/x/sync v0.7.0 // indirect golang.org/x/sys v0.21.0 // indirect golang.org/x/text v0.16.0 // indirect gonum.org/v1/gonum v0.15.0 // indirect google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect lukechampine.com/blake3 v1.3.0 // indirect moul.io/banner v1.0.1 // indirect moul.io/motd v1.0.0 // indirect ) ================================================ FILE: go.sum ================================================ bazil.org/fuse v0.0.0-20200117225306-7b5117fecadc h1:utDghgcjE8u+EBjHOgYT+dJPcnDF05KqWMBcjuJy510= bazil.org/fuse v0.0.0-20200117225306-7b5117fecadc/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM= berty.tech/go-ipfs-log v1.10.3-0.20240719141234-29e2d26e2aeb h1:FogPdtHCS/jQMX0iC9r/iQxVyYIFEpcoQvQrf6gxf0w= berty.tech/go-ipfs-log v1.10.3-0.20240719141234-29e2d26e2aeb/go.mod h1:9iZx/jWL+Su7j5ALhljjKZN/QScM4ONGs7yqqlcb/Qg= berty.tech/go-ipfs-repo-encrypted v1.3.1-0.20240722095251-c6b363b38785 h1:71O7eF4Hr22KKOk5y9Uzvt372siWoA55/DBj2DSbafA= berty.tech/go-ipfs-repo-encrypted v1.3.1-0.20240722095251-c6b363b38785/go.mod h1:JjEWS7xkbCQAm2NFt2JKFg3wx2Qkgr2jBMHRAgkfU10= berty.tech/go-orbit-db v1.22.2-0.20240719144258-ec7d1faaca68 h1:Y0TmVC11z+CQpQFoqyeOcrh/wgEIhbI/ZppfJOjuvDk= berty.tech/go-orbit-db v1.22.2-0.20240719144258-ec7d1faaca68/go.mod h1:UoKTs3bAL3Q1eCwj2VKA/LFspSFbacNeZsUkxDh3S3Q= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.63.0/go.mod h1:GmezbQc7T2snqkEXWfZ0sy0VfkB/ivI2DdtJL2DEmlg= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= cloud.google.com/go v0.105.0 h1:DNtEKRBAAzeS4KyIory52wWHuClNaXJ5x1F7xa4q+5Y= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/compute v1.13.0 h1:AYrLkB8NPdDRslNp4Jxmzrhdr03fUAIDbiGFjLWowoU= cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= contrib.go.opencensus.io/exporter/prometheus v0.4.2 h1:sqfsYl5GIY/L570iT+l93ehxaWJs2/OwXtiWwew3oAg= contrib.go.opencensus.io/exporter/prometheus v0.4.2/go.mod h1:dvEHbiKmgvbr5pjaF9fpw1KeYcjrnC1J8B+JKjsZyRQ= dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M= github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DataDog/zstd v1.4.1 h1:3oxKN3wbHibqx897utPC2LTQU4J+IHWWJO+glkAkpFM= github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/Jorropo/jsync v1.0.1 h1:6HgRolFZnsdfzRUj+ImB9og1JYOxQoReSywkHOGSaUU= github.com/Jorropo/jsync v1.0.1/go.mod h1:jCOZj3vrBCri3bSU3ErUYvevKlnbssrXeCivybS5ABQ= github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60= github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/VictoriaMetrics/fastcache v1.5.7 h1:4y6y0G8PRzszQUYIQHHssv/jgPHAb5qQuuDNdCbyAgw= github.com/VictoriaMetrics/fastcache v1.5.7/go.mod h1:ptDBkNMQI4RtmVo8VS/XwRY6RoTu1dAWCbrk+6WsEM8= github.com/aead/ecdh v0.2.0 h1:pYop54xVaq/CEREFEcukHRZfTdjiWvYIsZDXXrBapQQ= github.com/aead/ecdh v0.2.0/go.mod h1:a9HHtXuSo8J1Js1MwLQx2mBhkXMT6YwUmVVEY4tTB8U= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9 h1:ez/4by2iGztzR4L0zgAOR8lTQK9VlyBVVd7G4omaOQs= github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= github.com/alexbrainman/goissue34681 v0.0.0-20191006012335-3fc7a47baff5 h1:iW0a5ljuFxkLGPNem5Ui+KBjFJzKg4Fv2fnxe4dvzpM= github.com/alexbrainman/goissue34681 v0.0.0-20191006012335-3fc7a47baff5/go.mod h1:Y2QMoi1vgtOIfc+6DhrMOGkLoGzqSV2rKp4Sm+opsyA= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/berty/emitter-go v0.0.0-20221031144724-5dae963c3622 h1:kJqfCXKR5EJdh9HYh4rjYL3QvxjP5cnCssIU141m79c= github.com/berty/emitter-go v0.0.0-20221031144724-5dae963c3622/go.mod h1:G66sIy+q6BKIoKoKNqFU7sxSnrS5d8Z8meQ3Iu0ZJ4o= github.com/berty/go-libp2p-rendezvous v0.5.1 h1:6KnCOlyMIKAZq5COJeglWK5M8MhSJ2cKtMDf6x0KQm0= github.com/berty/go-libp2p-rendezvous v0.5.1/go.mod h1:gNhPX2RnxaGHLKxj/hWiaQKR2TwYnKxjtFXoVhzUBYE= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.22.1 h1:CnwP9LM/M9xuRrGSCGeMVs9iv09uMqwsVX7EeIpgV2c= github.com/btcsuite/btcd v0.22.1/go.mod h1:wqgTSL29+50LRkmOVknEdmt8ZojIzhuWvgu/iptuN7Y= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce h1:YtWJF7RHm2pYCvA5t0RPmAaLUhREsKuKd+SLhxFbFeQ= github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/buicongtan1997/protoc-gen-swagger-config v0.0.0-20200705084907-1342b78c1a7e h1:SHEegopzGCu0vkoUK98733r9TfviEO57VVPkYQ0G+WU= github.com/buicongtan1997/protoc-gen-swagger-config v0.0.0-20200705084907-1342b78c1a7e/go.mod h1:NQIP/fdybt3yyAPo2/ew6pJYzUb1EWizEblXkQOu+TE= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/ceramicnetwork/go-dag-jose v0.1.0 h1:yJ/HVlfKpnD3LdYP03AHyTvbm3BpPiz2oZiOeReJRdU= github.com/ceramicnetwork/go-dag-jose v0.1.0/go.mod h1:qYA1nYt0X8u4XoMAVoOV3upUVKtrxy/I670Dg5F0wjI= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cheggaaa/pb v1.0.29 h1:FckUN5ngEk2LpvuG0fw1GEFx6LtyY2pWI/Z2QgCnEYo= github.com/cheggaaa/pb v1.0.29/go.mod h1:W40334L7FMC5JKWldsTWbdGjLo0RxUKK73K+TuPxX30= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/containerd/cgroups v0.0.0-20201119153540-4cbc285b3327/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/crackcomm/go-gitignore v0.0.0-20231225121904-e25f5bc08668 h1:ZFUue+PNxmHlu7pYv+IYMtqlaO/0VwaGEqKepZf9JpA= github.com/crackcomm/go-gitignore v0.0.0-20231225121904-e25f5bc08668/go.mod h1:p1d6YEZWvFzEh4KLyvBcVSnrfNDDvK2zfK/4x2v/4pE= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cskr/pubsub v1.0.2 h1:vlOzMhl6PFn60gRlTQQsIfVwaPB/B/8MziK8FhEPt/0= github.com/cskr/pubsub v1.0.2/go.mod h1:/8MzYXk/NJAz782G8RPkFzXTZVu63VotefPnR9TIRis= github.com/daixiang0/gci v0.8.2 h1:4VLVDNdJ+wkXxz/nr5QRrbeK+JCvkMVqYjUWB5EnPF4= github.com/daixiang0/gci v0.8.2/go.mod h1:EpVfrztufwVgQRXjnX4zuNinEpLj5OmMjtu/+MB0V0c= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c h1:pFUpOrbxDR6AkioZ1ySsx5yxlDQZ8stG2b88gTPxgJU= github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6UhI8N9EjYm1c2odKpFpAYeR8dsBeM7PtzQhRgxRr9U= github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= github.com/dgraph-io/badger v1.6.2 h1:mNw0qs90GVgGGWylh0umH5iag1j6n/PeJtNvL6KY/x8= github.com/dgraph-io/badger v1.6.2/go.mod h1:JW2yswe3V058sS0kZ2h/AXeDSqFjxnZcRrVH//y2UQE= github.com/dgraph-io/badger/v2 v2.2007.3 h1:Sl9tQWz92WCbVSe8pj04Tkqlm2boW+KAxd+XSs58SQI= github.com/dgraph-io/badger/v2 v2.2007.3/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE= github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgraph-io/ristretto v0.0.3 h1:jh22xisGBjrEVnRZ1DVTpBVQm0Xndu8sMl0CWDzSIBI= github.com/dgraph-io/ristretto v0.0.3/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/eclipse/paho.mqtt.golang v1.4.2 h1:66wOzfUHSSI1zamx7jR6yMEI5EuHnT1G6rNA5PM12m4= github.com/eclipse/paho.mqtt.golang v1.4.2/go.mod h1:JGt0RsEwEX+Xa/agj90YJ9d9DH2b7upDZMK9HRbFvCA= github.com/elastic/gosigar v0.12.0/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= github.com/elastic/gosigar v0.14.2 h1:Dg80n8cr90OZ7x+bAax/QjoW/XqTI11RmA79ZwIm9/4= github.com/elastic/gosigar v0.14.2/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= github.com/elgris/jsondiff v0.0.0-20160530203242-765b5c24c302 h1:QV0ZrfBLpFc2KDk+a4LJefDczXnonRwrYrQJY/9L4dA= github.com/elgris/jsondiff v0.0.0-20160530203242-765b5c24c302/go.mod h1:qBlWZqWeVx9BjvqBsnC/8RUlAYpIFmPvgROcw0n1scE= github.com/emicklei/proto v1.6.13 h1:8iuAuKbFmFhkmstObb0EV/Hrn9W+x6EuV1y5Da8Ye9E= github.com/emicklei/proto v1.6.13/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A= github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5 h1:BBso6MBKW8ncyZLv37o+KNyy0HrrHgfnOaGQC2qvN+A= github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5/go.mod h1:JpoxHjuQauoxiFMl1ie8Xc/7TfLuMZ5eOCONd1sUBHg= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4= github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/flynn/noise v1.1.0 h1:KjPQoQCEFdZDiP03phOvGi11+SVVhBG2wOWAorLsstg= github.com/flynn/noise v1.1.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag= github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/uuid v4.3.1+incompatible h1:0/KbAdpx3UXAx1kEOWHJeOkpbgRFGHVgv+CFIY7dBJI= github.com/gofrs/uuid v4.3.1+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4= github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20240509144519-723abb6459b7 h1:velgFPYr1X9TDwLIfkV7fWqsFlf7TeP11M/7kPd/dVI= github.com/google/pprof v0.0.0-20240509144519-723abb6459b7/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/tink/go v1.7.0 h1:6Eox8zONGebBFcCBqkVmt60LaWZa6xg1cl/DwAh/J1w= github.com/google/tink/go v1.7.0/go.mod h1:GAUOd+QE3pgj9q8VKIGTCP33c/B7eb4NhxLcgTJZStM= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20190812055157-5d271430af9f h1:KMlcu9X58lhTA/KrfX8Bi1LQSO4pzoVjTiL3h4Jk+Zk= github.com/gopherjs/gopherjs v0.0.0-20190812055157-5d271430af9f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU= github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= github.com/hyperledger/aries-framework-go v0.1.9-0.20221202141134-083803ecf0a3 h1:r/jf1DTXG72uO5xK1VgZGywcjBlXh1E8kKnAt67bXJA= github.com/hyperledger/aries-framework-go v0.1.9-0.20221202141134-083803ecf0a3/go.mod h1:5lp5+NPjRngsjFLYYGg5mtkvw6I4Mr7CKz+wHYxROk0= github.com/hyperledger/aries-framework-go/spi v0.0.0-20221025204933-b807371b6f1e h1:SxbXlF39661T9w/L9PhVdtbJfJ51Pm4JYEEW6XfZHEQ= github.com/hyperledger/aries-framework-go/spi v0.0.0-20221025204933-b807371b6f1e/go.mod h1:oryUyWb23l/a3tAP9KW+GBbfcfqp9tZD4y5hSkFrkqI= github.com/hyperledger/ursa-wrapper-go v0.3.1 h1:Do+QrVNniY77YK2jTIcyWqj9rm/Yb5SScN0bqCjiibA= github.com/hyperledger/ursa-wrapper-go v0.3.1/go.mod h1:nPSAuMasIzSVciQo22PedBk4Opph6bJ6ia3ms7BH/mk= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/ipfs-shipyard/nopfs v0.0.12 h1:mvwaoefDF5VI9jyvgWCmaoTJIJFAfrbyQV5fJz35hlk= github.com/ipfs-shipyard/nopfs v0.0.12/go.mod h1:mQyd0BElYI2gB/kq/Oue97obP4B3os4eBmgfPZ+hnrE= github.com/ipfs-shipyard/nopfs/ipfs v0.13.2-0.20231027223058-cde3b5ba964c h1:7UynTbtdlt+w08ggb1UGLGaGjp1mMaZhoTZSctpn5Ak= github.com/ipfs-shipyard/nopfs/ipfs v0.13.2-0.20231027223058-cde3b5ba964c/go.mod h1:6EekK/jo+TynwSE/ZOiOJd4eEvRXoavEC3vquKtv4yI= github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs= github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0= github.com/ipfs/boxo v0.20.0 h1:umUl7q1v5g5AX8FPLTnZBvvagLmT+V0Tt61EigP81ec= github.com/ipfs/boxo v0.20.0/go.mod h1:mwttn53Eibgska2DhVIj7ln3UViq7MVHRxOMb+ehSDM= github.com/ipfs/go-bitfield v1.1.0 h1:fh7FIo8bSwaJEh6DdTWbCeZ1eqOaOkKFI74SCnsWbGA= github.com/ipfs/go-bitfield v1.1.0/go.mod h1:paqf1wjq/D2BBmzfTVFlJQ9IlFOZpg422HL0HqsGWHU= github.com/ipfs/go-bitswap v0.11.0 h1:j1WVvhDX1yhG32NTC9xfxnqycqYIlhzEzLXG/cU1HyQ= github.com/ipfs/go-bitswap v0.11.0/go.mod h1:05aE8H3XOU+LXpTedeAS0OZpcO1WFsj5niYQH9a1Tmk= github.com/ipfs/go-block-format v0.0.3/go.mod h1:4LmD4ZUw0mhO+JSKdpWwrzATiEfM7WWgQ8H5l6P8MVk= github.com/ipfs/go-block-format v0.2.0 h1:ZqrkxBA2ICbDRbK8KJs/u0O3dlp6gmAuuXUJNiW1Ycs= github.com/ipfs/go-block-format v0.2.0/go.mod h1:+jpL11nFx5A/SPpsoBn6Bzkra/zaArfSmsknbPMYgzM= github.com/ipfs/go-blockservice v0.5.2 h1:in9Bc+QcXwd1apOVM7Un9t8tixPKdaHQFdLSUM1Xgk8= github.com/ipfs/go-blockservice v0.5.2/go.mod h1:VpMblFEqG67A/H2sHKAemeH9vlURVavlysbdUI632yk= github.com/ipfs/go-cid v0.0.3/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= github.com/ipfs/go-cid v0.0.4/go.mod h1:4LLaPOQwmk5z9LBgQnpkivrx8BJjUyGwTXCd5Xfj6+M= github.com/ipfs/go-cid v0.0.7/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I= github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= github.com/ipfs/go-cidutil v0.1.0 h1:RW5hO7Vcf16dplUU60Hs0AKDkQAVPVplr7lk97CFL+Q= github.com/ipfs/go-cidutil v0.1.0/go.mod h1:e7OEVBMIv9JaOxt9zaGEmAoSlXW9jdFZ5lP/0PwcfpA= github.com/ipfs/go-datastore v0.1.0/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE= github.com/ipfs/go-datastore v0.1.1/go.mod h1:w38XXW9kVFNp57Zj5knbKWM2T+KOZCGDRVNdgPHtbHw= github.com/ipfs/go-datastore v0.5.0/go.mod h1:9zhEApYMTl17C8YDp7JmU7sQZi2/wqiYh73hakZ90Bk= github.com/ipfs/go-datastore v0.5.1/go.mod h1:9zhEApYMTl17C8YDp7JmU7sQZi2/wqiYh73hakZ90Bk= github.com/ipfs/go-datastore v0.6.0 h1:JKyz+Gvz1QEZw0LsX1IBn+JFCJQH4SJVFtM4uWU0Myk= github.com/ipfs/go-datastore v0.6.0/go.mod h1:rt5M3nNbSO/8q1t4LNkLyUwRs8HupMeN/8O4Vn9YAT8= github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps= github.com/ipfs/go-ds-badger v0.0.7/go.mod h1:qt0/fWzZDoPW6jpQeqUjR5kBfhDNB65jd9YlmAvpQBk= github.com/ipfs/go-ds-badger v0.3.0 h1:xREL3V0EH9S219kFFueOYJJTcjgNSZ2HY1iSvN7U1Ro= github.com/ipfs/go-ds-badger v0.3.0/go.mod h1:1ke6mXNqeV8K3y5Ak2bAA0osoTfmxUdupVCGm4QUIek= github.com/ipfs/go-ds-badger2 v0.1.3 h1:Zo9JicXJ1DmXTN4KOw7oPXkspZ0AWHcAFCP1tQKnegg= github.com/ipfs/go-ds-badger2 v0.1.3/go.mod h1:TPhhljfrgewjbtuL/tczP8dNrBYwwk+SdPYbms/NO9w= github.com/ipfs/go-ds-flatfs v0.5.1 h1:ZCIO/kQOS/PSh3vcF1H6a8fkRGS7pOfwfPdx4n/KJH4= github.com/ipfs/go-ds-flatfs v0.5.1/go.mod h1:RWTV7oZD/yZYBKdbVIFXTX2fdY2Tbvl94NsWqmoyAX4= github.com/ipfs/go-ds-leveldb v0.1.0/go.mod h1:hqAW8y4bwX5LWcCtku2rFNX3vjDZCy5LZCg+cSZvYb8= github.com/ipfs/go-ds-leveldb v0.5.0 h1:s++MEBbD3ZKc9/8/njrn4flZLnCuY9I79v94gBUNumo= github.com/ipfs/go-ds-leveldb v0.5.0/go.mod h1:d3XG9RUDzQ6V4SHi8+Xgj9j1XuEk1z82lquxrVbml/Q= github.com/ipfs/go-ds-measure v0.2.0 h1:sG4goQe0KDTccHMyT45CY1XyUbxe5VwTKpg2LjApYyQ= github.com/ipfs/go-ds-measure v0.2.0/go.mod h1:SEUD/rE2PwRa4IQEC5FuNAmjJCyYObZr9UvVh8V3JxE= github.com/ipfs/go-ds-sql v0.3.0 h1:PLBbl0Rt0tBwWhQ0b3GCQbH+Bgd6aj2srKG6vJ7nYl4= github.com/ipfs/go-ds-sql v0.3.0/go.mod h1:jE3bhmuUnMPXFftc4NEAiPUfgiwiv7fIdjozuX+m1/E= github.com/ipfs/go-fs-lock v0.0.7 h1:6BR3dajORFrFTkb5EpCUFIAypsoxpGpDSVUdFwzgL9U= github.com/ipfs/go-fs-lock v0.0.7/go.mod h1:Js8ka+FNYmgQRLrRXzU3CB/+Csr1BwrRilEcvYrHhhc= github.com/ipfs/go-ipfs-blockstore v1.3.1 h1:cEI9ci7V0sRNivqaOr0elDsamxXFxJMMMy7PTTDQNsQ= github.com/ipfs/go-ipfs-blockstore v1.3.1/go.mod h1:KgtZyc9fq+P2xJUiCAzbRdhhqJHvsw8u2Dlqy2MyRTE= github.com/ipfs/go-ipfs-blocksutil v0.0.1 h1:Eh/H4pc1hsvhzsQoMEP3Bke/aW5P5rVM1IWFJMcGIPQ= github.com/ipfs/go-ipfs-blocksutil v0.0.1/go.mod h1:Yq4M86uIOmxmGPUHv/uI7uKqZNtLb449gwKqXjIsnRk= github.com/ipfs/go-ipfs-chunker v0.0.5 h1:ojCf7HV/m+uS2vhUGWcogIIxiO5ubl5O57Q7NapWLY8= github.com/ipfs/go-ipfs-chunker v0.0.5/go.mod h1:jhgdF8vxRHycr00k13FM8Y0E+6BoalYeobXmUyTreP8= github.com/ipfs/go-ipfs-cmds v0.11.0 h1:6AsTKwbVxwzrOkq2x89e6jYMGxzYqjt/WbAam69HZQE= github.com/ipfs/go-ipfs-cmds v0.11.0/go.mod h1:DHp7YfJlOK+2IS07nk+hFmbKHK52tc29W38CaAgWHpk= github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= github.com/ipfs/go-ipfs-delay v0.0.1 h1:r/UXYyRcddO6thwOnhiznIAiSvxMECGgtv35Xs1IeRQ= github.com/ipfs/go-ipfs-delay v0.0.1/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= github.com/ipfs/go-ipfs-ds-help v1.1.1 h1:B5UJOH52IbcfS56+Ul+sv8jnIV10lbjLF5eOO0C66Nw= github.com/ipfs/go-ipfs-ds-help v1.1.1/go.mod h1:75vrVCkSdSFidJscs8n4W+77AtTpCIAdDGAwjitJMIo= github.com/ipfs/go-ipfs-exchange-interface v0.2.1 h1:jMzo2VhLKSHbVe+mHNzYgs95n0+t0Q69GQ5WhRDZV/s= github.com/ipfs/go-ipfs-exchange-interface v0.2.1/go.mod h1:MUsYn6rKbG6CTtsDp+lKJPmVt3ZrCViNyH3rfPGsZ2E= github.com/ipfs/go-ipfs-exchange-offline v0.3.0 h1:c/Dg8GDPzixGd0MC8Jh6mjOwU57uYokgWRFidfvEkuA= github.com/ipfs/go-ipfs-exchange-offline v0.3.0/go.mod h1:MOdJ9DChbb5u37M1IcbrRB02e++Z7521fMxqCNRrz9s= github.com/ipfs/go-ipfs-keystore v0.1.0 h1:gfuQUO/cyGZgZIHE6OrJas4OnwuxXCqJG7tI0lrB5Qc= github.com/ipfs/go-ipfs-keystore v0.1.0/go.mod h1:LvLw7Qhnb0RlMOfCzK6OmyWxICip6lQ06CCmdbee75U= github.com/ipfs/go-ipfs-pq v0.0.3 h1:YpoHVJB+jzK15mr/xsWC574tyDLkezVrDNeaalQBsTE= github.com/ipfs/go-ipfs-pq v0.0.3/go.mod h1:btNw5hsHBpRcSSgZtiNm/SLj5gYIZ18AKtv3kERkRb4= github.com/ipfs/go-ipfs-redirects-file v0.1.1 h1:Io++k0Vf/wK+tfnhEh63Yte1oQK5VGT2hIEYpD0Rzx8= github.com/ipfs/go-ipfs-redirects-file v0.1.1/go.mod h1:tAwRjCV0RjLTjH8DR/AU7VYvfQECg+lpUy2Mdzv7gyk= github.com/ipfs/go-ipfs-routing v0.3.0 h1:9W/W3N+g+y4ZDeffSgqhgo7BsBSJwPMcyssET9OWevc= github.com/ipfs/go-ipfs-routing v0.3.0/go.mod h1:dKqtTFIql7e1zYsEuWLyuOU+E0WJWW8JjbTPLParDWo= github.com/ipfs/go-ipfs-util v0.0.1/go.mod h1:spsl5z8KUnrve+73pOhSVZND1SIxPW5RyBCNzQxlJBc= github.com/ipfs/go-ipfs-util v0.0.2/go.mod h1:CbPtkWJzjLdEcezDns2XYaehFVNXG9zrdrtMecczcsQ= github.com/ipfs/go-ipfs-util v0.0.3 h1:2RFdGez6bu2ZlZdI+rWfIdbQb1KudQp3VGwPtdNCmE0= github.com/ipfs/go-ipfs-util v0.0.3/go.mod h1:LHzG1a0Ig4G+iZ26UUOMjHd+lfM84LZCrn17xAKWBvs= github.com/ipfs/go-ipld-cbor v0.1.0 h1:dx0nS0kILVivGhfWuB6dUpMa/LAwElHPw1yOGYopoYs= github.com/ipfs/go-ipld-cbor v0.1.0/go.mod h1:U2aYlmVrJr2wsUBU67K4KgepApSZddGRDWBYR0H4sCk= github.com/ipfs/go-ipld-format v0.6.0 h1:VEJlA2kQ3LqFSIm5Vu6eIlSxD/Ze90xtc4Meten1F5U= github.com/ipfs/go-ipld-format v0.6.0/go.mod h1:g4QVMTn3marU3qXchwjpKPKgJv+zF+OlaKMyhJ4LHPg= github.com/ipfs/go-ipld-git v0.1.1 h1:TWGnZjS0htmEmlMFEkA3ogrNCqWjIxwr16x1OsdhG+Y= github.com/ipfs/go-ipld-git v0.1.1/go.mod h1:+VyMqF5lMcJh4rwEppV0e6g4nCCHXThLYYDpKUkJubI= github.com/ipfs/go-ipld-legacy v0.2.1 h1:mDFtrBpmU7b//LzLSypVrXsD8QxkEWxu5qVxN99/+tk= github.com/ipfs/go-ipld-legacy v0.2.1/go.mod h1:782MOUghNzMO2DER0FlBR94mllfdCJCkTtDtPM51otM= github.com/ipfs/go-libipfs v0.6.2 h1:QUf3kS3RrCjgtE0QW2d18PFFfOLeEt24Ft892ipLzRI= github.com/ipfs/go-libipfs v0.6.2/go.mod h1:FmhKgxMOQA572TK5DA3MZ5GL44ZqsMHIrkgK4gLn4A8= github.com/ipfs/go-log v0.0.1/go.mod h1:kL1d2/hzSpI0thNYjiKfjanbVNU+IIGA/WnNESY9leM= github.com/ipfs/go-log v1.0.3/go.mod h1:OsLySYkwIbiSUR/yBTdv1qPtcE4FW3WPWk/ewz9Ru+A= github.com/ipfs/go-log v1.0.5 h1:2dOuUCB1Z7uoczMWgAyDck5JLb72zHzrMnGnCNNbvY8= github.com/ipfs/go-log v1.0.5/go.mod h1:j0b8ZoR+7+R99LD9jZ6+AJsrzkPbSXbZfGakb5JPtIo= github.com/ipfs/go-log/v2 v2.0.3/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0= github.com/ipfs/go-log/v2 v2.0.5/go.mod h1:eZs4Xt4ZUJQFM3DlanGhy7TkwwawCZcSByscwkWG+dw= github.com/ipfs/go-log/v2 v2.1.3/go.mod h1:/8d0SH3Su5Ooc31QlL1WysJhvyOTDCjcCZ9Axpmri6g= github.com/ipfs/go-log/v2 v2.3.0/go.mod h1:QqGoj30OTpnKaG/LKTGTxoP2mmQtjVMEnK72gynbe/g= github.com/ipfs/go-log/v2 v2.5.0/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI= github.com/ipfs/go-log/v2 v2.5.1 h1:1XdUzF7048prq4aBjDQQ4SL5RxftpRGdXhNRwKSAlcY= github.com/ipfs/go-log/v2 v2.5.1/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI= github.com/ipfs/go-merkledag v0.11.0 h1:DgzwK5hprESOzS4O1t/wi6JDpyVQdvm9Bs59N/jqfBY= github.com/ipfs/go-merkledag v0.11.0/go.mod h1:Q4f/1ezvBiJV0YCIXvt51W/9/kqJGH4I1LsA7+djsM4= github.com/ipfs/go-metrics-interface v0.0.1 h1:j+cpbjYvu4R8zbleSs36gvB7jR+wsL2fGD6n0jO4kdg= github.com/ipfs/go-metrics-interface v0.0.1/go.mod h1:6s6euYU4zowdslK0GKHmqaIZ3j/b/tL7HTWtJ4VPgWY= github.com/ipfs/go-peertaskqueue v0.8.1 h1:YhxAs1+wxb5jk7RvS0LHdyiILpNmRIRnZVztekOF0pg= github.com/ipfs/go-peertaskqueue v0.8.1/go.mod h1:Oxxd3eaK279FxeydSPPVGHzbwVeHjatZ2GA8XD+KbPU= github.com/ipfs/go-unixfs v0.4.5 h1:wj8JhxvV1G6CD7swACwSKYa+NgtdWC1RUit+gFnymDU= github.com/ipfs/go-unixfs v0.4.5/go.mod h1:BIznJNvt/gEx/ooRMI4Us9K8+qeGO7vx1ohnbk8gjFg= github.com/ipfs/go-unixfsnode v1.9.0 h1:ubEhQhr22sPAKO2DNsyVBW7YB/zA8Zkif25aBvz8rc8= github.com/ipfs/go-unixfsnode v1.9.0/go.mod h1:HxRu9HYHOjK6HUqFBAi++7DVoWAHn0o4v/nZ/VA+0g8= github.com/ipfs/go-verifcid v0.0.3 h1:gmRKccqhWDocCRkC+a59g5QW7uJw5bpX9HWBevXa0zs= github.com/ipfs/go-verifcid v0.0.3/go.mod h1:gcCtGniVzelKrbk9ooUSX/pM3xlH73fZZJDzQJRvOUw= github.com/ipfs/kubo v0.29.0 h1:J5G5le0/gYkx8qLN/zxDl0LcEXKbHZyMh4FCuQN1nVo= github.com/ipfs/kubo v0.29.0/go.mod h1:mLhuve/44BxEX5ujEihviRXiaxdlrja3kjJgEs2WhK0= github.com/ipld/go-car v0.6.2 h1:Hlnl3Awgnq8icK+ze3iRghk805lu8YNq3wlREDTF2qc= github.com/ipld/go-car v0.6.2/go.mod h1:oEGXdwp6bmxJCZ+rARSkDliTeYnVzv3++eXajZ+Bmr8= github.com/ipld/go-car/v2 v2.13.1 h1:KnlrKvEPEzr5IZHKTXLAEub+tPrzeAFQVRlSQvuxBO4= github.com/ipld/go-car/v2 v2.13.1/go.mod h1:QkdjjFNGit2GIkpQ953KBwowuoukoM75nP/JI1iDJdo= github.com/ipld/go-codec-dagpb v1.6.0 h1:9nYazfyu9B1p3NAgfVdpRco3Fs2nFC72DqVsMj6rOcc= github.com/ipld/go-codec-dagpb v1.6.0/go.mod h1:ANzFhfP2uMJxRBr8CE+WQWs5UsNa0pYtmKZ+agnUw9s= github.com/ipld/go-ipld-prime v0.11.0/go.mod h1:+WIAkokurHmZ/KwzDOMUuoeJgaRQktHtEaLglS3ZeV8= github.com/ipld/go-ipld-prime v0.14.1/go.mod h1:QcE4Y9n/ZZr8Ijg5bGPT0GqYWgZ1704nH0RDcQtgTP0= github.com/ipld/go-ipld-prime v0.21.0 h1:n4JmcpOlPDIxBcY037SVfpd1G+Sj1nKZah0m6QH9C2E= github.com/ipld/go-ipld-prime v0.21.0/go.mod h1:3RLqy//ERg/y5oShXXdx5YIp50cFGOanyMctpPjsvxQ= github.com/ipld/go-ipld-prime/storage/bsadapter v0.0.0-20230102063945-1a409dc236dd h1:gMlw/MhNr2Wtp5RwGdsW23cs+yCuj9k2ON7i9MiJlRo= github.com/ipld/go-ipld-prime/storage/bsadapter v0.0.0-20230102063945-1a409dc236dd/go.mod h1:wZ8hH8UxeryOs4kJEJaiui/s00hDSbE37OKsL47g+Sw= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jbenet/go-cienv v0.1.0 h1:Vc/s0QbQtoxX8MwwSLWWh+xNNZvM3Lw7NsTcHrvvhMc= github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA= github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABoLk/+KKHggpk= github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk= github.com/jbenet/goprocess v0.0.0-20160826012719-b497e2f366b8/go.mod h1:Ly/wlsjFq/qrU3Rar62tu1gASgGw6chQbSh/XgIIXCY= github.com/jbenet/goprocess v0.1.3/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= github.com/jbenet/goprocess v0.1.4 h1:DRGOFReOMqqDNXwW70QkacFW0YN9QnwLV0Vqk+3oU0o= github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a h1:zPPuIq2jAWWPTrGt70eK/BSch+gFAGrNzecsoENgu2o= github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a/go.mod h1:yL958EeXv8Ylng6IfnvG4oflryUi3vgA3xPs9hmII1s= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/juju/fslock v0.0.0-20160525022230-4d5c94c67b4b h1:FQ7+9fxhyp82ks9vAuyPzG0/vVbWwMwLJ+P6yJI5FN8= github.com/juju/fslock v0.0.0-20160525022230-4d5c94c67b4b/go.mod h1:HMcgvsgd0Fjj4XXDkbjdmlbI505rUPBs6WBMYg2pXks= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d/go.mod h1:P2viExyCEfeWGU259JnaQ34Inuec4R38JCyBx2edgD0= github.com/kilic/bls12-381 v0.1.1-0.20210503002446-7b7597926c69 h1:kMJlf8z8wUcpyI+FQJIdGjAhfTww1y0AbQEv86bpVQI= github.com/kilic/bls12-381 v0.1.1-0.20210503002446-7b7597926c69/go.mod h1:tlkavyke+Ac7h8R3gZIjI5LKBcvMlSWnXNMgT3vZXo8= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/koron/go-ssdp v0.0.4 h1:1IDwrghSKYM7yLf7XCzbByg2sJ/JcNOZRXS2jczTwz0= github.com/koron/go-ssdp v0.0.4/go.mod h1:oDXq+E5IL5q0U8uSBcoAXzTzInwy5lEgC91HoKtbmZk= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU= github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/libp2p/go-buffer-pool v0.0.1/go.mod h1:xtyIz9PMobb13WaxR6Zo1Pd1zXJKYg0a8KiIvDp3TzQ= github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM= github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= github.com/libp2p/go-cidranger v1.1.0 h1:ewPN8EZ0dd1LSnrtuwd4709PXVcITVeuwbag38yPW7c= github.com/libp2p/go-cidranger v1.1.0/go.mod h1:KWZTfSr+r9qEo9OkI9/SIEeAtw+NNoU0dXIXt15Okic= github.com/libp2p/go-doh-resolver v0.4.0 h1:gUBa1f1XsPwtpE1du0O+nnZCUqtG7oYi7Bb+0S7FQqw= github.com/libp2p/go-doh-resolver v0.4.0/go.mod h1:v1/jwsFusgsWIGX/c6vCRrnJ60x7bhTiq/fs2qt0cAg= github.com/libp2p/go-flow-metrics v0.0.1/go.mod h1:Iv1GH0sG8DtYN3SVJ2eG221wMiNpZxBdp967ls1g+k8= github.com/libp2p/go-flow-metrics v0.0.3/go.mod h1:HeoSNUrOJVK1jEpDqVEiUOIXqhbnS27omG0uWU5slZs= github.com/libp2p/go-flow-metrics v0.1.0 h1:0iPhMI8PskQwzh57jB9WxIuIOQ0r+15PChFGkx3Q3WM= github.com/libp2p/go-flow-metrics v0.1.0/go.mod h1:4Xi8MX8wj5aWNDAZttg6UPmc0ZrnFNsMtpsYUClFtro= github.com/libp2p/go-libp2p v0.34.1 h1:fxn9vyLo7vJcXQRNvdRbyPjbzuQgi2UiqC8hEbn8a18= github.com/libp2p/go-libp2p v0.34.1/go.mod h1:snyJQix4ET6Tj+LeI0VPjjxTtdWpeOhYt5lEY0KirkQ= github.com/libp2p/go-libp2p-asn-util v0.4.1 h1:xqL7++IKD9TBFMgnLPZR6/6iYhawHKHl950SO9L6n94= github.com/libp2p/go-libp2p-asn-util v0.4.1/go.mod h1:d/NI6XZ9qxw67b4e+NgpQexCIiFYJjErASrYW4PFDN8= github.com/libp2p/go-libp2p-core v0.2.4/go.mod h1:STh4fdfa5vDYr0/SzYYeqnt+E6KfEV5VxfIrm0bcI0g= github.com/libp2p/go-libp2p-core v0.3.0/go.mod h1:ACp3DmS3/N64c2jDzcV429ukDpicbL6+TrrxANBjPGw= github.com/libp2p/go-libp2p-gostream v0.6.0 h1:QfAiWeQRce6pqnYfmIVWJFXNdDyfiR/qkCnjyaZUPYU= github.com/libp2p/go-libp2p-gostream v0.6.0/go.mod h1:Nywu0gYZwfj7Jc91PQvbGU8dIpqbQQkjWgDuOrFaRdA= github.com/libp2p/go-libp2p-http v0.5.0 h1:+x0AbLaUuLBArHubbbNRTsgWz0RjNTy6DJLOxQ3/QBc= github.com/libp2p/go-libp2p-http v0.5.0/go.mod h1:glh87nZ35XCQyFsdzZps6+F4HYI6DctVFY5u1fehwSg= github.com/libp2p/go-libp2p-kad-dht v0.25.2 h1:FOIk9gHoe4YRWXTu8SY9Z1d0RILol0TrtApsMDPjAVQ= github.com/libp2p/go-libp2p-kad-dht v0.25.2/go.mod h1:6za56ncRHYXX4Nc2vn8z7CZK0P4QiMcrn77acKLM2Oo= github.com/libp2p/go-libp2p-kbucket v0.3.1/go.mod h1:oyjT5O7tS9CQurok++ERgc46YLwEpuGoFq9ubvoUOio= github.com/libp2p/go-libp2p-kbucket v0.6.3 h1:p507271wWzpy2f1XxPzCQG9NiN6R6lHL9GiSErbQQo0= github.com/libp2p/go-libp2p-kbucket v0.6.3/go.mod h1:RCseT7AH6eJWxxk2ol03xtP9pEHetYSPXOaJnOiD8i0= github.com/libp2p/go-libp2p-peerstore v0.1.4/go.mod h1:+4BDbDiiKf4PzpANZDAT+knVdLxvqh7hXOujessqdzs= github.com/libp2p/go-libp2p-pubsub v0.11.1-0.20240711152552-e508d8643ddb h1:Ux/fNS52HowibmSbtEDzeKiu4N5gFklPv/1myFh/VVE= github.com/libp2p/go-libp2p-pubsub v0.11.1-0.20240711152552-e508d8643ddb/go.mod h1:QEb+hEV9WL9wCiUAnpY29FZR6W3zK8qYlaml8R4q6gQ= github.com/libp2p/go-libp2p-pubsub-router v0.6.0 h1:D30iKdlqDt5ZmLEYhHELCMRj8b4sFAqrUcshIUvVP/s= github.com/libp2p/go-libp2p-pubsub-router v0.6.0/go.mod h1:FY/q0/RBTKsLA7l4vqC2cbRbOvyDotg8PJQ7j8FDudE= github.com/libp2p/go-libp2p-record v0.2.0 h1:oiNUOCWno2BFuxt3my4i1frNrt7PerzB3queqa1NkQ0= github.com/libp2p/go-libp2p-record v0.2.0/go.mod h1:I+3zMkvvg5m2OcSdoL0KPljyJyvNDFGKX7QdlpYUcwk= github.com/libp2p/go-libp2p-routing-helpers v0.7.3 h1:u1LGzAMVRK9Nqq5aYDVOiq/HaB93U9WWczBzGyAC5ZY= github.com/libp2p/go-libp2p-routing-helpers v0.7.3/go.mod h1:cN4mJAD/7zfPKXBcs9ze31JGYAZgzdABEm+q/hkswb8= github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA= github.com/libp2p/go-libp2p-testing v0.12.0/go.mod h1:KcGDRXyN7sQCllucn1cOOS+Dmm7ujhfEyXQL5lvkcPg= github.com/libp2p/go-libp2p-xor v0.1.0 h1:hhQwT4uGrBcuAkUGXADuPltalOdpf9aag9kaYNT2tLA= github.com/libp2p/go-libp2p-xor v0.1.0/go.mod h1:LSTM5yRnjGZbWNTA/hRwq2gGFrvRIbQJscoIL/u6InY= github.com/libp2p/go-msgio v0.0.4/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ= github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0= github.com/libp2p/go-msgio v0.3.0/go.mod h1:nyRM819GmVaF9LX3l03RMh10QdOroF++NBbxAb0mmDM= github.com/libp2p/go-nat v0.2.0 h1:Tyz+bUFAYqGyJ/ppPPymMGbIgNRH+WqC5QrT5fKrrGk= github.com/libp2p/go-nat v0.2.0/go.mod h1:3MJr+GRpRkyT65EpVPBstXLvOlAPzUVlG6Pwg9ohLJk= github.com/libp2p/go-netroute v0.2.1 h1:V8kVrpD8GK0Riv15/7VN6RbUQ3URNZVosw7H2v9tksU= github.com/libp2p/go-netroute v0.2.1/go.mod h1:hraioZr0fhBjG0ZRXJJ6Zj2IVEVNx6tDTFQfSmcq7mQ= github.com/libp2p/go-openssl v0.0.3/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= github.com/libp2p/go-openssl v0.0.4/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= github.com/libp2p/go-reuseport v0.4.0 h1:nR5KU7hD0WxXCJbmw7r2rhRYruNRl2koHw8fQscQm2s= github.com/libp2p/go-reuseport v0.4.0/go.mod h1:ZtI03j/wO5hZVDFo2jKywN6bYKWLOy8Se6DrI2E1cLU= github.com/libp2p/go-yamux/v4 v4.0.1 h1:FfDR4S1wj6Bw2Pqbc8Uz7pCxeRBPbwsBbEdfwiCypkQ= github.com/libp2p/go-yamux/v4 v4.0.1/go.mod h1:NWjl8ZTLOGlozrXSOZ/HlfG++39iKNnM5wwmtQP1YB4= github.com/libp2p/zeroconf/v2 v2.2.0 h1:Cup06Jv6u81HLhIj1KasuNM/RHHrJ8T7wOTS4+Tv53Q= github.com/libp2p/zeroconf/v2 v2.2.0/go.mod h1:fuJqLnUwZTshS3U/bMRJ3+ow/v9oid1n0DmyYyNO1Xs= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/lyft/protoc-gen-star/v2 v2.0.3 h1:/3+/2sWyXeMLzKd1bX+ixWKgEMsULrIivpDsuaF441o= github.com/lyft/protoc-gen-star/v2 v2.0.3/go.mod h1:amey7yeodaJhXSbf/TlLvWiqQfLOSpEk//mLlc+axEk= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU= github.com/maruel/circular v0.0.0-20200815005550-36e533b830e9 h1:d8OcZrg9dmqfBsHRDGP2QarJlj/1p0YI/NylTf2LYqo= github.com/maruel/circular v0.0.0-20200815005550-36e533b830e9/go.mod h1:AEsb24YMiJiSqh8Cs8kRGJxDDXKEkveJ7nxYUeYibEc= github.com/maruel/ut v1.0.2 h1:mQTlQk3jubTbdTcza+hwoZQWhzcvE4L6K6RTtAFlA1k= github.com/maruel/ut v1.0.2/go.mod h1:RV8PwPD9dd2KFlnlCc/DB2JVvkXmyaalfc5xvmSrRSs= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.8 h1:3tS41NlGYSmhhe/8fhGRzc+z3AYCw1Fe1WAyLuujKs0= github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mdomke/git-semver/v5 v5.0.0 h1:By50HK/pTLR64WUUmDVtQNrVNDXsCehujhjBTAIyCHk= github.com/mdomke/git-semver/v5 v5.0.0/go.mod h1:+f5KQvxYk4WbjLPgYujVa+97Gx0dbrc4fxIK7F6fRf0= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs= github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk= github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c h1:bzE/A84HN25pxAuk9Eej1Kz9OUelF97nAc82bDquQI8= github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c/go.mod h1:0SQS9kMwD2VsyFEB++InYyBJroV/FRmBgcydeSUcJms= github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b h1:z78hV3sbSMAUoyUMM0I83AUIT6Hu17AWfgjzIbtrYFc= github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b/go.mod h1:lxPUiZwKoFL8DUUmalo2yJJUCxbPKtm8OKfqr2/FTNU= github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc h1:PTfri+PuQmWDqERdnNMiD9ZejrlswWrCpBEZgWOiTrc= github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc/go.mod h1:cGKTAVKx4SxOuR/czcZ/E2RSJ3sfHs8FpHhQ5CWMf9s= github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.1 h1:FVzMWA5RllMAKIdUSC8mdWo3XtwoecrH79BY70sEEpE= github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA= github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE= github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI= github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM= github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0= github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4= github.com/multiformats/go-multiaddr v0.1.0/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo= github.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y95ncPj0vFwVF6k6wJ4= github.com/multiformats/go-multiaddr v0.12.4 h1:rrKqpY9h+n80EwhhC/kkcunCZZ7URIF8yN1WEUt2Hvc= github.com/multiformats/go-multiaddr v0.12.4/go.mod h1:sBXrNzucqkFJhvKOiwwLyqamGa/P5EIXNPLovyhQCII= github.com/multiformats/go-multiaddr-dns v0.3.0/go.mod h1:mNzQ4eTGDg0ll1N9jKPOUogZPoJ30W8a7zk66FQPpdQ= github.com/multiformats/go-multiaddr-dns v0.3.1 h1:QgQgR+LQVt3NPTjbrLLpsaT2ufAA2y0Mkk+QRVJbW3A= github.com/multiformats/go-multiaddr-dns v0.3.1/go.mod h1:G/245BRQ6FJGmryJCrOuTdB37AMA5AMOVuO6NY3JwTk= github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E= github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo= github.com/multiformats/go-multiaddr-net v0.1.1/go.mod h1:5JNbcfBOP4dnhoZOv10JJVkJO0pCCEf8mTnipAo2UZQ= github.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs= github.com/multiformats/go-multibase v0.0.3/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc= github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g= github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk= github.com/multiformats/go-multicodec v0.3.0/go.mod h1:qGGaQmioCDh+TeFOnxrbU0DaIPw8yFgAZgFG0V7p1qQ= github.com/multiformats/go-multicodec v0.9.0 h1:pb/dlPnzee/Sxv/j4PmkDRxCOi3hXTz3IbPKOXWJkmg= github.com/multiformats/go-multicodec v0.9.0/go.mod h1:L3QTQvMIaVBkXOXXtVmYE+LI16i14xuaojr/H7Ai54k= github.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U= github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= github.com/multiformats/go-multihash v0.0.10/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= github.com/multiformats/go-multihash v0.0.13/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= github.com/multiformats/go-multihash v0.0.14/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= github.com/multiformats/go-multihash v0.0.15/go.mod h1:D6aZrWNLFTV/ynMpKsNtB40mJzmCl4jb1alC0OvHiHg= github.com/multiformats/go-multihash v0.1.0/go.mod h1:RJlXsxt6vHGaia+S8We0ErjhojtKzPP2AH4+kYM7k84= github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U= github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM= github.com/multiformats/go-multistream v0.5.0 h1:5htLSLl7lvJk3xx3qT/8Zm9J4K8vEOf/QGkvOGQAyiE= github.com/multiformats/go-multistream v0.5.0/go.mod h1:n6tMZiwiP2wUsR8DgfDWw1dydlEqV3l6N3/GBsX6ILA= github.com/multiformats/go-varint v0.0.1/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= github.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8= github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= github.com/mutecomm/go-sqlcipher/v4 v4.4.2 h1:eM10bFtI4UvibIsKr10/QT7Yfz+NADfjZYh0GKrXUNc= github.com/mutecomm/go-sqlcipher/v4 v4.4.2/go.mod h1:mF2UmIpBnzFeBdu/ypTDb/LdbS0nk0dfSN1WUsWTjMA= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-proto-validators v0.0.0-20180403085117-0950a7990007 h1:28i1IjGcx8AofiB4N3q5Yls55VEaitzuEPkFJEVgGkA= github.com/mwitkow/go-proto-validators v0.0.0-20180403085117-0950a7990007/go.mod h1:m2XC9Qq0AlmmVksL6FktJCdTYyLk7V3fKyp0sl1yWQo= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY= github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/ginkgo/v2 v2.17.3 h1:oJcvKpIb7/8uLpDDtnQuf18xVnwKp8DTD7DQ6gTd/MU= github.com/onsi/ginkgo/v2 v2.17.3/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.33.0 h1:snPCflnZrpMsy94p4lXVEkHo12lmPnc3vY5XBbreexE= github.com/onsi/gomega v1.33.0/go.mod h1:+925n5YtiFsLzzafLUHzVMBpvvRAzrydIBiSIxjX3wY= github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk= github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= github.com/openzipkin/zipkin-go v0.4.3 h1:9EGwpqkgnwdEIJ+Od7QVSEIH+ocmm5nPat0G7sjsSdg= github.com/openzipkin/zipkin-go v0.4.3/go.mod h1:M9wCJZFWCo2RiY+o1eBCEMe0Dp2S5LDHcMZmk3RmK7c= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys= github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9 h1:1/WtZae0yGtPq+TI6+Tv1WTxkukpXeMlviSxvL7SRgk= github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9/go.mod h1:x3N5drFsm2uilKKuuYo6LdyD8vZAW55sH/9w+pbo1sw= github.com/peterbourgon/ff/v3 v3.0.0 h1:eQzEmNahuOjQXfuegsKQTSTDbf4dNvr/eNLrmJhiH7M= github.com/peterbourgon/ff/v3 v3.0.0/go.mod h1:UILIFjRH5a/ar8TjXYLTkIvSvekZqPm5Eb/qbGk6CT0= github.com/pion/datachannel v1.5.6 h1:1IxKJntfSlYkpUj8LlYRSWpYiTTC02nUrOE8T3DqGeg= github.com/pion/datachannel v1.5.6/go.mod h1:1eKT6Q85pRnr2mHiWHxJwO50SfZRtWHTsNIVb/NfGW4= github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= github.com/pion/dtls/v2 v2.2.11 h1:9U/dpCYl1ySttROPWJgqWKEylUdT0fXp/xst6JwY5Ks= github.com/pion/dtls/v2 v2.2.11/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE= github.com/pion/ice/v2 v2.3.24 h1:RYgzhH/u5lH0XO+ABatVKCtRd+4U1GEaCXSMjNr13tI= github.com/pion/ice/v2 v2.3.24/go.mod h1:KXJJcZK7E8WzrBEYnV4UtqEZsGeWfHxsNqhVcVvgjxw= github.com/pion/interceptor v0.1.29 h1:39fsnlP1U8gw2JzOFWdfCU82vHvhW9o0rZnZF56wF+M= github.com/pion/interceptor v0.1.29/go.mod h1:ri+LGNjRUc5xUNtDEPzfdkmSqISixVTBF/z/Zms/6T4= github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= github.com/pion/mdns v0.0.12 h1:CiMYlY+O0azojWDmxdNr7ADGrnZ+V6Ilfner+6mSVK8= github.com/pion/mdns v0.0.12/go.mod h1:VExJjv8to/6Wqm1FXK+Ii/Z9tsVk/F5sD/N70cnYFbk= github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= github.com/pion/rtcp v1.2.12/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9L4= github.com/pion/rtcp v1.2.14 h1:KCkGV3vJ+4DAJmvP0vaQShsb0xkRfWkO540Gy102KyE= github.com/pion/rtcp v1.2.14/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9L4= github.com/pion/rtp v1.8.3/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= github.com/pion/rtp v1.8.6 h1:MTmn/b0aWWsAzux2AmP8WGllusBVw4NPYPVFFd7jUPw= github.com/pion/rtp v1.8.6/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= github.com/pion/sctp v1.8.13/go.mod h1:YKSgO/bO/6aOMP9LCie1DuD7m+GamiK2yIiPM6vH+GA= github.com/pion/sctp v1.8.16 h1:PKrMs+o9EMLRvFfXq59WFsC+V8mN1wnKzqrv+3D/gYY= github.com/pion/sctp v1.8.16/go.mod h1:P6PbDVA++OJMrVNg2AL3XtYHV4uD6dvfyOovCgMs0PE= github.com/pion/sdp/v3 v3.0.9 h1:pX++dCHoHUwq43kuwf3PyJfHlwIj4hXA7Vrifiq0IJY= github.com/pion/sdp/v3 v3.0.9/go.mod h1:B5xmvENq5IXJimIO4zfp6LAe1fD9N+kFv+V/1lOdz8M= github.com/pion/srtp/v2 v2.0.18 h1:vKpAXfawO9RtTRKZJbG4y0v1b11NZxQnxRl85kGuUlo= github.com/pion/srtp/v2 v2.0.18/go.mod h1:0KJQjA99A6/a0DOVTu1PhDSw0CXF2jTkqOoMg3ODqdA= github.com/pion/stun v0.6.1 h1:8lp6YejULeHBF8NmV8e2787BogQhduZugh5PdhDyyN4= github.com/pion/stun v0.6.1/go.mod h1:/hO7APkX4hZKu/D0f2lHzNyvdkTGtIy3NDmLR7kSz/8= github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g= github.com/pion/transport/v2 v2.2.2/go.mod h1:OJg3ojoBJopjEeECq2yJdXH9YVrUJ1uQ++NjXLOUorc= github.com/pion/transport/v2 v2.2.3/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0= github.com/pion/transport/v2 v2.2.4/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0= github.com/pion/transport/v2 v2.2.5 h1:iyi25i/21gQck4hfRhomF6SktmUQjRsRW4WJdhfc3Kc= github.com/pion/transport/v2 v2.2.5/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0= github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0= github.com/pion/transport/v3 v3.0.2 h1:r+40RJR25S9w3jbA6/5uEPTzcdn7ncyU44RWCbHkLg4= github.com/pion/transport/v3 v3.0.2/go.mod h1:nIToODoOlb5If2jF9y2Igfx3PFYWfuXi37m0IlWa/D0= github.com/pion/turn/v2 v2.1.3/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY= github.com/pion/turn/v2 v2.1.6 h1:Xr2niVsiPTB0FPtt+yAWKFUkU1eotQbGgpTIld4x1Gc= github.com/pion/turn/v2 v2.1.6/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY= github.com/pion/webrtc/v3 v3.2.40 h1:Wtfi6AZMQg+624cvCXUuSmrKWepSB7zfgYDOYqsSOVU= github.com/pion/webrtc/v3 v3.2.40/go.mod h1:M1RAe3TNTD1tzyvqHrbVODfwdPGSXOUo/OgpoGGJqFY= github.com/piprate/json-gold v0.4.2 h1:Rq8V+637HOFcj20KdTqW/g/llCwX2qtau0g5d1pD79o= github.com/piprate/json-gold v0.4.2/go.mod h1:OK1z7UgtBZk06n2cDE2OSq1kffmjFFp5/2yhLLCz9UM= github.com/pkg/diff v0.0.0-20200914180035-5b29258ca4f7/go.mod h1:zO8QMzTeZd5cpnIkz/Gn6iK0jDfGicM1nynOkkPIl28= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/polydawn/refmt v0.0.0-20201211092308-30ac6d18308e/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o= github.com/polydawn/refmt v0.89.0 h1:ADJTApkvkeBZsN0tBTx8QjpD9JkmxbKp0cxfr9qszm4= github.com/polydawn/refmt v0.89.0/go.mod h1:/zvteZs/GwLtCgZ4BL6CBsk9IKIlexP43ObX9AxTqTw= github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/pquerna/cachecontrol v0.1.0 h1:yJMy84ti9h/+OEWa752kBTKv4XC30OtVVHYv/8cTqKc= github.com/pquerna/cachecontrol v0.1.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQnrHV5K9mBcUI= github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/common v0.35.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= github.com/prometheus/common v0.53.0 h1:U2pL9w9nmJwJDa4qqLQ3ZaePJ6ZTwt7cMD3AG3+aLCE= github.com/prometheus/common v0.53.0/go.mod h1:BrxBKv3FWBIGXw89Mg1AeBq7FSyRzXWI3l3e7W3RN5U= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= github.com/prometheus/procfs v0.15.0 h1:A82kmvXJq2jTu5YUhSGNlYoxh85zLnKgPz4bMZgI5Ek= github.com/prometheus/procfs v0.15.0/go.mod h1:Y0RJ/Y5g5wJpkTisOtqwDSo4HwhGmLB4VQSw2sQJLHk= github.com/prometheus/statsd_exporter v0.22.7 h1:7Pji/i2GuhK6Lu7DHrtTkFmNBCudCPT1pX2CziuyQR0= github.com/prometheus/statsd_exporter v0.22.7/go.mod h1:N/TevpjkIh9ccs6nuzY3jQn9dFqnUakOjnEuMPJJJnI= github.com/pseudomuto/protoc-gen-doc v1.5.1 h1:Ah259kcrio7Ix1Rhb6u8FCaOkzf9qRBqXnvAufg061w= github.com/pseudomuto/protoc-gen-doc v1.5.1/go.mod h1:XpMKYg6zkcpgfpCfQ8GcWBDRtRxOmMR5w7pz4Xo+dYM= github.com/pseudomuto/protokit v0.2.0 h1:hlnBDcy3YEDXH7kc9gV+NLaN0cDzhDvD1s7Y6FZ8RpM= github.com/pseudomuto/protokit v0.2.0/go.mod h1:2PdH30hxVHsup8KpBTOXTBeMVhJZVio3Q8ViKSAXT0Q= github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= github.com/quic-go/quic-go v0.44.0 h1:So5wOr7jyO4vzL2sd8/pD9Kesciv91zSk8BoFngItQ0= github.com/quic-go/quic-go v0.44.0/go.mod h1:z4cx/9Ny9UtGITIPzmPTXh1ULfOyWh4qGQlpnPcWmek= github.com/quic-go/webtransport-go v0.8.0 h1:HxSrwun11U+LlmwpgM1kEqIqH90IT4N8auv/cD7QFJg= github.com/quic-go/webtransport-go v0.8.0/go.mod h1:N99tjprW432Ut5ONql/aUhSLT0YVSlwHohQsuac9WaM= github.com/raulk/go-watchdog v1.3.0 h1:oUmdlHxdkXRJlwfG0O9omj8ukerm8MEQavSiDTEtBsk= github.com/raulk/go-watchdog v1.3.0/go.mod h1:fIvOnLbF0b0ZwkB9YU4mOW9Did//4vPZtDqv66NfsMU= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo= github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= github.com/samber/lo v1.39.0 h1:4gTz1wUhNYLhFSKl6O+8peW0v2F4BCY034GRpU9WnuA= github.com/samber/lo v1.39.0/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0= github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw= github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI= github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU= github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag= github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg= github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw= github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y= github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q= github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ= github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I= github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0= github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ= github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk= github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs= github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg= github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM= github.com/smola/gocompat v0.2.0/go.mod h1:1B0MlxbmoZNo3h8guHp8HztB3BSYR5itql9qtVc0ypY= github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= github.com/spf13/afero v1.5.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY= github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/square/go-jose/v3 v3.0.0-20200630053402-0a67ce9b0693 h1:wD1IWQwAhdWclCwaf6DdzgCAe9Bfz1M+4AHRd7N786Y= github.com/square/go-jose/v3 v3.0.0-20200630053402-0a67ce9b0693/go.mod h1:6hSY48PjDm4UObWmGLyJE9DxYVKTgR9kbCspXXJEhcU= github.com/src-d/envconfig v1.0.0/go.mod h1:Q9YQZ7BKITldTBnoxsE5gOeB5y66RyPXeue/R4aaNBc= github.com/srikrsna/protoc-gen-gotag v1.0.1 h1:zMDkplPjcpIOafgfXTD1BqCrMGycXcomV794MIKKi9s= github.com/srikrsna/protoc-gen-gotag v1.0.1/go.mod h1:HiXK5kcp/ZRnNPahuJm3tzfGDoD8xzvLNdg5/PYKq7Q= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stvp/go-udp-testing v0.0.0-20201019212854-469649b16807/go.mod h1:7jxmlfBCDBXRzr0eAQJ48XC1hBu1np4CS5+cHEYfwpc= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502/go.mod h1:p9lPsd+cx33L3H9nNoecRRxPssFKUwwI50I3pZ0yT+8= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/teserakt-io/golang-ed25519 v0.0.0-20210104091850-3888c087a4c8 h1:RBkacARv7qY5laaXGlF4wFB/tk5rnthhPb8oIBGoagY= github.com/teserakt-io/golang-ed25519 v0.0.0-20210104091850-3888c087a4c8/go.mod h1:9PdLyPiZIiW3UopXyRnPYyjUXSpiQNHRLu8fOsR3o8M= github.com/tj/assert v0.0.3 h1:Df/BlaZ20mq6kuai7f5z2TvPFiwC3xaWJSDQNiIS3Rk= github.com/tj/assert v0.0.3/go.mod h1:Ne6X72Q+TB1AteidzQncjw9PabbMp4PBMZ1k+vd1Pvk= github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c h1:u6SKchux2yDvFQnDHS3lPnIRmfVJ5Sxy3ao2SIdysLQ= github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM= github.com/ucarion/urlpath v0.0.0-20200424170820-7ccc79b76bbb h1:Ywfo8sUltxogBpFuMOFRrrSifO788kAFxmvVw31PtQQ= github.com/ucarion/urlpath v0.0.0-20200424170820-7ccc79b76bbb/go.mod h1:ikPs9bRWicNw3S7XpJ8sK/smGwU9WcSVU3dy9qahYBM= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= github.com/wangjia184/sortedset v0.0.0-20160527075905-f5d03557ba30/go.mod h1:YkocrP2K2tcw938x9gCOmT5G5eCD6jsTz0SZuyAqwIE= github.com/warpfork/go-testmark v0.3.0/go.mod h1:jhEf8FVxd+F17juRubpmut64NEG6I2rgkUhlcqqXwE0= github.com/warpfork/go-testmark v0.9.0/go.mod h1:jhEf8FVxd+F17juRubpmut64NEG6I2rgkUhlcqqXwE0= github.com/warpfork/go-testmark v0.12.1 h1:rMgCpJfwy1sJ50x0M0NgyphxYYPMOODIJHhsXyEHU0s= github.com/warpfork/go-testmark v0.12.1/go.mod h1:kHwy7wfvGSPh1rQJYKayD4AbtNaeyZdcGi9tNJTaa5Y= github.com/warpfork/go-wish v0.0.0-20200122115046-b9ea61034e4a/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0 h1:GDDkbFiaK8jsSDJfjId/PEGEShv6ugrt4kYsC5UIDaQ= github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= github.com/whyrusleeping/base32 v0.0.0-20170828182744-c30ac30633cc h1:BCPnHtcboadS0DvysUuJXZ4lWVv5Bh5i7+tbIyi+ck4= github.com/whyrusleeping/base32 v0.0.0-20170828182744-c30ac30633cc/go.mod h1:r45hJU7yEoA81k6MWNhpMj/kms0n14dkzkxYHoB96UM= github.com/whyrusleeping/cbor v0.0.0-20171005072247-63513f603b11 h1:5HZfQkwe0mIfyDmc1Em5GqlNRzcdtlv4HTNmdpt7XH0= github.com/whyrusleeping/cbor v0.0.0-20171005072247-63513f603b11/go.mod h1:Wlo/SzPmxVp6vXpGt/zaXhHH0fn4IxgqZc82aKg6bpQ= github.com/whyrusleeping/cbor-gen v0.1.1 h1:eKfcJIoxivjMtwfCfmJAqSF56MHcWqyIScXwaC1VBgw= github.com/whyrusleeping/cbor-gen v0.1.1/go.mod h1:pM99HXyEbSQHcosHc0iW7YFmwnscr+t9Te4ibko05so= github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f h1:jQa4QT2UP9WYv2nzyawpKMOCl+Z/jW7djv2/J50lj9E= github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f/go.mod h1:p9UJB6dDgdPgMJZs7UjUOdulKyRr9fqkS+6JKAInPy8= github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 h1:EKhdznlJHPMoKr0XTrX+IlJs1LH3lyx2nfr1dOlZ79k= github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1/go.mod h1:8UvriyWtv5Q5EOgjHaSseUEdkQfvwFv1I/In/O2M9gc= github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc/go.mod h1:bopw91TMyo8J3tvftk8xmU2kPmlrt4nScJQZU2hE5EM= github.com/whyrusleeping/go-sysinfo v0.0.0-20190219211824-4a357d4b90b1 h1:ctS9Anw/KozviCCtK6VWMz5kPL9nbQzbQY4yfqlIV4M= github.com/whyrusleeping/go-sysinfo v0.0.0-20190219211824-4a357d4b90b1/go.mod h1:tKH72zYNt/exx6/5IQO6L9LoQ0rEjd5SbbWaDTs9Zso= github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 h1:E9S12nwJwEOXe2d6gT6qxdvqMnNq+VnSsKPgm2ZZNds= github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7/go.mod h1:X2c0RVCI1eSUFI8eLcY3c0423ykwiUdxLJtkDvruhjI= github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0 h1:Xs2Ncz0gNihqu9iosIZ5SkBbWo5T8JhhLJFMQL1qmLI= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0/go.mod h1:vy+2G/6NvVMpwGX/NyLqcC41fxepnuKHk16E6IZUcJc= go.opentelemetry.io/otel v1.26.0 h1:LQwgL5s/1W7YiiRwxf03QGnWLb2HW4pLiAhaA5cZXBs= go.opentelemetry.io/otel v1.26.0/go.mod h1:UmLkJHUAidDval2EICqBMbnAd0/m2vmpf/dAM+fvFs4= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.26.0 h1:1u/AyyOqAWzy+SkPxDpahCNZParHV8Vid1RnI2clyDE= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.26.0/go.mod h1:z46paqbJ9l7c9fIPCXTqTGwhQZ5XoTIsfeFYWboizjs= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.26.0 h1:Waw9Wfpo/IXzOI8bCB7DIk+0JZcqqsyn1JFnAc+iam8= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.26.0/go.mod h1:wnJIG4fOqyynOnnQF/eQb4/16VlX2EJAHhHgqIqWfAo= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.26.0 h1:1wp/gyxsuYtuE/JFxsQRtcCDtMrO2qMvlfXALU5wkzI= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.26.0/go.mod h1:gbTHmghkGgqxMomVQQMur1Nba4M0MQ8AYThXDUjsJ38= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.26.0 h1:0W5o9SzoR15ocYHEQfvfipzcNog1lBxOLfnex91Hk6s= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.26.0/go.mod h1:zVZ8nz+VSggWmnh6tTsJqXQ7rU4xLwRtna1M4x5jq58= go.opentelemetry.io/otel/exporters/zipkin v1.26.0 h1:sBk6A62GgcQRwcxcBwRMPkqeuSizcpHkXyZNyP281Fw= go.opentelemetry.io/otel/exporters/zipkin v1.26.0/go.mod h1:fLzYtPUxPFzu7rSqhYsCxYheT2dNoPjtKovCLzLm07w= go.opentelemetry.io/otel/metric v1.26.0 h1:7S39CLuY5Jgg9CrnA9HHiEjGMF/X2VHvoXGgSllRz30= go.opentelemetry.io/otel/metric v1.26.0/go.mod h1:SY+rHOI4cEawI9a7N1A4nIg/nTQXe1ccCNWYOJUrpX4= go.opentelemetry.io/otel/sdk v1.26.0 h1:Y7bumHf5tAiDlRYFmGqetNcLaVUZmh4iYfmGxtmz7F8= go.opentelemetry.io/otel/sdk v1.26.0/go.mod h1:0p8MXpqLeJ0pzcszQQN4F0S5FVjBLgypeGSngLsmirs= go.opentelemetry.io/otel/trace v1.26.0 h1:1ieeAUb4y0TE26jUFrCIXKpTuVK7uJGN9/Z/2LP5sQA= go.opentelemetry.io/otel/trace v1.26.0/go.mod h1:4iDxvGDQuUkHve82hJJ8UqrwswHYsZuWCBllGV2U2y0= go.opentelemetry.io/proto/otlp v1.2.0 h1:pVeZGk7nXDC9O2hncA6nHldxEjm6LByfA2aN8IOkz94= go.opentelemetry.io/proto/otlp v1.2.0/go.mod h1:gGpR8txAl5M03pDhMC79G6SdqNV26naRm/KDsgaHD8A= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.8.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/dig v1.17.1 h1:Tga8Lz8PcYNsWsyHMZ1Vm0OQOUaJNDyvPImgbAu9YSc= go.uber.org/dig v1.17.1/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= go.uber.org/fx v1.21.1 h1:RqBh3cYdzZS0uqwVeEjOX2p73dddLpym315myy/Bpb0= go.uber.org/fx v1.21.1/go.mod h1:HT2M7d7RHo+ebKGh9NRcrsrHHfpZ60nW3QRubMRfv48= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.14.1/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= go.uber.org/zap v1.20.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= go4.org v0.0.0-20200411211856-f5505b9728dd/go.mod h1:CIiUVy99QCPfoE13bO4EZaz5GZMZXMSBGhxRdsvzbkg= go4.org v0.0.0-20230225012048-214862532bf5 h1:nifaUDeh+rPaBCMPMQHZmvJf+QdpLFnuQPwx+LxVmtc= go4.org v0.0.0-20230225012048-214862532bf5/go.mod h1:F57wTi5Lrj6WLyswp5EYV1ncrEbFGHD4hhz6S1ZYeaU= golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190227160552-c95aed5357e7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo= golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210426080607-c94f62235c83/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181130052023-1c3d964395ce/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200806022845-90696ccdc692/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= gonum.org/v1/gonum v0.15.0 h1:2lYxjRbTYyxkJxlhC+LvJIx3SsANPdRybu1tGj9/OrQ= gonum.org/v1/gonum v0.15.0/go.mod h1:xzZVBJBtS+Mz4q0Yl2LJTk+OxOg4jiXZ7qBoM0uISGo= google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd h1:OjndDrsik+Gt+e6fs45z9AxiewiKyLKYpA45W5Kpkks= google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 h1:F29+wU6Ee6qgu9TddPgooOdaqsxTMunOoj8KA5yuS5A= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1/go.mod h1:5KF+wpkbTSbGcR9zteSqZV6fqFOWBl4Yde8En8MryZA= google.golang.org/grpc/examples v0.0.0-20200922230038-4e932bbcb079 h1:unzgkDPNegIn/czOcgxzQaTzEzOiBH1V1j55rsEzVEg= google.golang.org/grpc/examples v0.0.0-20200922230038-4e932bbcb079/go.mod h1:Lh55/1hxmVHEkOvSIQ2uj0P12QyOCUNyRwnUlSS13hw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/src-d/go-cli.v0 v0.0.0-20181105080154-d492247bbc0d/go.mod h1:z+K8VcOYVYcSwSjGebuDL6176A1XskgbtNl64NSg+n8= gopkg.in/src-d/go-log.v1 v1.0.1/go.mod h1:GN34hKP0g305ysm2/hctJ0Y8nWP3zxXXJ8GFabTyABE= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= lukechampine.com/blake3 v1.1.6/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA= lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE= lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= moul.io/banner v1.0.1 h1:+WsemGLhj2pOajw2eR5VYjLhOIqs0XhIRYchzTyMLk0= moul.io/banner v1.0.1/go.mod h1:XwvIGKkhKRKyN1vIdmR5oaKQLIkMhkMqrsHpS94QzAU= moul.io/godev v1.7.0/go.mod h1:5lgSpI1oH7xWpLl2Ew/Nsgk8DiNM6FzN9WV9+lgW8RQ= moul.io/motd v1.0.0 h1:Trk4fPibDfPJf2iCBSQC8ws7Q02sMwivQdVEFAjCPto= moul.io/motd v1.0.0/go.mod h1:39rvZ0lC2oRhHDY2VoPyZ8r70VKqeJye3QAxjeLDJso= moul.io/openfiles v1.2.0 h1:oAmBX0ChBBsKERFfTRyLi9JkjOvEv9E474BEL/wVq44= moul.io/openfiles v1.2.0/go.mod h1:FR9BZ1mw7VE0uZN6HVJcA16Ee2nTDG/YZUyiGM/T2Rw= moul.io/srand v1.6.1 h1:SJ335F+54ivLdlH7wH52Rtyv0Ffos6DpsF5wu3ZVMXU= moul.io/srand v1.6.1/go.mod h1:P2uaZB+GFstFNo8sEj6/U8FRV1n25kD0LLckFpJ+qvc= moul.io/testman v1.5.0 h1:tN1XEzLxYh8ZYy1wET6leWufnTl7BcZELkSNiro/yEo= moul.io/testman v1.5.0/go.mod h1:b4/5+lMsMDJtwuh25Cr0eVJ5Y4B2lSPfkzDtfct070g= moul.io/u v1.6.0/go.mod h1:yd3/IoYRIJaZWAJV2rYHvM2EPp/Pp0zSNraB5IPX+hw= moul.io/u v1.27.0 h1:rF0p184mludn2DzL0unA8Gf/mFWMBerdqOh8cyuQYzQ= moul.io/u v1.27.0/go.mod h1:ggYDXxUjoHpfDsMPD3STqkUZTyA741PZiQhSd+7kRnA= moul.io/zapfilter v1.7.0 h1:7aFrG4N72bDH9a2BtYUuUaDS981Dxu3qybWfeqaeBDU= moul.io/zapfilter v1.7.0/go.mod h1:M+N2s+qZiA+bzRoyKMVRxyuERijS2ovi2pnMyiOGMvc= moul.io/zapring v1.3.3 h1:N2QPn6qTMBWjh842UPxdjj2UW+uH/foXohgGCPZDlM8= moul.io/zapring v1.3.3/go.mod h1:UvlTrdjeHtSqdjkGXwAxIfpaQ/ai4I+ccRASFxflcJE= mvdan.cc/gofumpt v0.4.0 h1:JVf4NN1mIpHogBj7ABpgOyZc65/UUOkKQFkoURsz4MM= mvdan.cc/gofumpt v0.4.0/go.mod h1:PljLOHDeZqgS8opHRKLzp2It2VBuSdteAgqUfzMTxlQ= pgregory.net/rapid v0.4.7 h1:MTNRktPuv5FNqOO151TM9mDTa+XHcX6ypYeISDVD14g= pgregory.net/rapid v0.4.7/go.mod h1:UYpPVyjFHzYBGHIxLFoupi8vwk6rXNzRY9OMvVxFIOU= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= ================================================ FILE: group.go ================================================ package weshnet import ( "github.com/libp2p/go-libp2p/core/crypto" "google.golang.org/protobuf/proto" "berty.tech/weshnet/v2/pkg/errcode" "berty.tech/weshnet/v2/pkg/protocoltypes" ) const CurrentGroupVersion = 1 // NewGroupMultiMember creates a new Group object and an invitation to be used by // the first member of the group func NewGroupMultiMember() (*protocoltypes.Group, crypto.PrivKey, error) { return protocoltypes.NewGroupMultiMember() } func getAndFilterGroupDeviceChainKeyAddedPayload(m *protocoltypes.GroupMetadata, localMemberPublicKey crypto.PubKey) (crypto.PubKey, []byte, error) { if m == nil || m.EventType != protocoltypes.EventType_EventTypeGroupDeviceChainKeyAdded { return nil, nil, errcode.ErrCode_ErrInvalidInput } s := &protocoltypes.GroupDeviceChainKeyAdded{} if err := proto.Unmarshal(m.Payload, s); err != nil { return nil, nil, errcode.ErrCode_ErrDeserialization.Wrap(err) } senderDevicePubKey, err := crypto.UnmarshalEd25519PublicKey(s.DevicePk) if err != nil { return nil, nil, errcode.ErrCode_ErrDeserialization.Wrap(err) } destMemberPubKey, err := crypto.UnmarshalEd25519PublicKey(s.DestMemberPk) if err != nil { return nil, nil, errcode.ErrCode_ErrDeserialization.Wrap(err) } if !localMemberPublicKey.Equals(destMemberPubKey) { return nil, nil, errcode.ErrCode_ErrGroupSecretOtherDestMember } return senderDevicePubKey, s.Payload, nil } ================================================ FILE: group_context.go ================================================ package weshnet import ( "context" "encoding/base64" "fmt" "sync" "sync/atomic" "time" "github.com/libp2p/go-libp2p/core/crypto" "go.uber.org/zap" "google.golang.org/protobuf/proto" "berty.tech/go-orbit-db/stores" "berty.tech/weshnet/v2/pkg/errcode" "berty.tech/weshnet/v2/pkg/ipfsutil" "berty.tech/weshnet/v2/pkg/logutil" "berty.tech/weshnet/v2/pkg/protocoltypes" "berty.tech/weshnet/v2/pkg/secretstore" ) type GroupContext struct { ctx context.Context cancel context.CancelFunc group *protocoltypes.Group metadataStore *MetadataStore messageStore *MessageStore secretStore secretstore.SecretStore ownMemberDevice secretstore.OwnMemberDevice logger *zap.Logger closed uint32 tasks sync.WaitGroup devicesAdded map[string]chan struct{} muDevicesAdded sync.RWMutex selfAnnounced chan struct{} selfAnnouncedOnce sync.Once } func (gc *GroupContext) SecretStore() secretstore.SecretStore { return gc.secretStore } func (gc *GroupContext) MessageStore() *MessageStore { return gc.messageStore } func (gc *GroupContext) MetadataStore() *MetadataStore { return gc.metadataStore } func (gc *GroupContext) Group() *protocoltypes.Group { return gc.group } func (gc *GroupContext) MemberPubKey() crypto.PubKey { return gc.ownMemberDevice.Member() } func (gc *GroupContext) DevicePubKey() crypto.PubKey { return gc.ownMemberDevice.Device() } func (gc *GroupContext) Close() error { gc.cancel() // @NOTE(gfanton): wait for active tasks to end, doing this we avoid to do // some operations on a closed store gc.tasks.Wait() // mark group context has closed atomic.StoreUint32(&gc.closed, 1) // @FIXME(gfanton): should we really handle store closing here ? gc.metadataStore.Close() gc.messageStore.Close() gc.logger.Debug("group context closed", zap.String("groupID", gc.group.GroupIDAsString())) return nil } func (gc *GroupContext) IsClosed() bool { return atomic.LoadUint32(&gc.closed) != 0 } func NewContextGroup(group *protocoltypes.Group, metadataStore *MetadataStore, messageStore *MessageStore, secretStore secretstore.SecretStore, memberDevice secretstore.OwnMemberDevice, logger *zap.Logger) *GroupContext { ctx, cancel := context.WithCancel(context.Background()) if logger == nil { logger = zap.NewNop() } return &GroupContext{ ctx: ctx, cancel: cancel, group: group, metadataStore: metadataStore, messageStore: messageStore, secretStore: secretStore, ownMemberDevice: memberDevice, logger: logger.With(logutil.PrivateString("group-id", fmt.Sprintf("%.6s", base64.StdEncoding.EncodeToString(group.PublicKey)))), closed: 0, devicesAdded: make(map[string]chan struct{}), selfAnnounced: make(chan struct{}), } } func (gc *GroupContext) ActivateGroupContext(contactPK crypto.PubKey) (err error) { ctx := gc.ctx // start watching for GroupMetadataEvent to send secret and register // chainkey of new members. { m := gc.MetadataStore() sub, err := m.EventBus().Subscribe(new(*protocoltypes.GroupMetadataEvent)) if err != nil { return fmt.Errorf("unable to subscribe to group metadata event: %w", err) } gc.tasks.Add(1) go func() { defer gc.tasks.Done() // ultimately, mark bg task has done defer sub.Close() for { var evt any select { case <-ctx.Done(): return case evt = <-sub.Out(): } // @TODO(gfanton): should we handle this in a sub gorouting ? e := evt.(*protocoltypes.GroupMetadataEvent) // start := time.Now() if err := gc.handleGroupMetadataEvent(e); err != nil { gc.logger.Error("unable to handle EventTypeGroupDeviceSecretAdded", zap.Error(err)) } // if t := time.Since(start).Milliseconds(); t > 0 { // fmt.Printf("elapsed: %dms\n", t) // } } }() } // send secret and register key from existing members. // we should wait until all the events have been retrieved. { var wgExistingMembers sync.WaitGroup wgExistingMembers.Add(2) go func() { start := time.Now() gc.fillMessageKeysHolderUsingPreviousData() wgExistingMembers.Done() gc.logger.Info(fmt.Sprintf("FillMessageKeysHolderUsingPreviousData took %s", time.Since(start))) }() go func() { start := time.Now() gc.sendSecretsToExistingMembers(contactPK) wgExistingMembers.Done() gc.logger.Info(fmt.Sprintf("SendSecretsToExistingMembers took %s", time.Since(start))) }() wgExistingMembers.Wait() } start := time.Now() op, err := gc.MetadataStore().AddDeviceToGroup(gc.ctx) if err != nil { return fmt.Errorf("unable to add device to groupo: %w", err) } gc.logger.Info(fmt.Sprintf("AddDeviceToGroup took %s", time.Since(start))) if op != nil { // Waiting for async events to be handled select { case <-ctx.Done(): return ctx.Err() case <-gc.selfAnnounced: // device has been selfAnnounced } } return nil } func (gc *GroupContext) handleGroupMetadataEvent(e *protocoltypes.GroupMetadataEvent) (err error) { switch e.Metadata.EventType { case protocoltypes.EventType_EventTypeGroupMemberDeviceAdded: event := &protocoltypes.GroupMemberDeviceAdded{} if err := proto.Unmarshal(e.Event, event); err != nil { gc.logger.Error("unable to unmarshal payload", zap.Error(err)) } memberPK, err := crypto.UnmarshalEd25519PublicKey(event.MemberPk) if err != nil { return fmt.Errorf("unable to unmarshal sender member pk: %w", err) } if memberPK.Equals(gc.ownMemberDevice.Member()) { gc.selfAnnouncedOnce.Do(func() { close(gc.selfAnnounced) }) // mark has self announced } if _, err := gc.MetadataStore().SendSecret(gc.ctx, memberPK); err != nil { if !errcode.Is(err, errcode.ErrCode_ErrGroupSecretAlreadySentToMember) { return fmt.Errorf("unable to send secret to member: %w", err) } } case protocoltypes.EventType_EventTypeGroupDeviceChainKeyAdded: senderPublicKey, encryptedDeviceChainKey, err := getAndFilterGroupDeviceChainKeyAddedPayload(e.Metadata, gc.ownMemberDevice.Member()) switch err { case nil: // ok case errcode.ErrCode_ErrInvalidInput, errcode.ErrCode_ErrGroupSecretOtherDestMember: // @FIXME(gfanton): should we log this ? return nil default: return fmt.Errorf("an error occurred while opening device secrets: %w", err) } if err = gc.SecretStore().RegisterChainKey(gc.ctx, gc.Group(), senderPublicKey, encryptedDeviceChainKey); err != nil { return fmt.Errorf("unable to register chain key: %w", err) } if rawPK, err := senderPublicKey.Raw(); err == nil { // A new chainKey has been registered, notify watcher go gc.notifyDeviceAdded(rawPK) // process queued message and check if cached messages can be opened with it gc.MessageStore().ProcessMessageQueueForDevicePK(gc.ctx, rawPK) } } return nil } func (gc *GroupContext) fillMessageKeysHolderUsingPreviousData() { publishedSecrets := gc.metadataStoreListSecrets() for senderPublicKey, encryptedSecret := range publishedSecrets { if err := gc.SecretStore().RegisterChainKey(gc.ctx, gc.Group(), senderPublicKey, encryptedSecret); err != nil { gc.logger.Error("unable to register chain key", zap.Error(err)) continue } // A new chainKey is registered, check if cached messages can be opened with it if rawPK, err := senderPublicKey.Raw(); err == nil { gc.MessageStore().ProcessMessageQueueForDevicePK(gc.ctx, rawPK) } } } func (gc *GroupContext) metadataStoreListSecrets() map[crypto.PubKey][]byte { publishedSecrets := map[crypto.PubKey][]byte{} m := gc.MetadataStore() metadatas, err := m.ListEvents(gc.ctx, nil, nil, false) if err != nil { return nil } for metadata := range metadatas { if metadata == nil { continue } pk, encryptedDeviceChainKey, err := getAndFilterGroupDeviceChainKeyAddedPayload(metadata.Metadata, gc.MemberPubKey()) if errcode.Is(err, errcode.ErrCode_ErrInvalidInput) || errcode.Is(err, errcode.ErrCode_ErrGroupSecretOtherDestMember) { continue } if err != nil { gc.logger.Error("unable to open chain key", zap.Error(err)) continue } publishedSecrets[pk] = encryptedDeviceChainKey } return publishedSecrets } func (gc *GroupContext) sendSecretsToExistingMembers(contact crypto.PubKey) { members := gc.MetadataStore().ListMembers() // Force sending secret to contact member in contact group if gc.group.GroupType == protocoltypes.GroupType_GroupTypeContact && len(members) < 2 && contact != nil { // Check if contact member is already listed found := false for _, member := range members { if member.Equals(contact) { found = true } } // If not listed, add it to the list if !found { members = append(members, contact) } } for _, pk := range members { rawPK, err := pk.Raw() if err != nil { gc.logger.Error("failed to serialize pk", zap.Error(err)) continue } if _, err := gc.MetadataStore().SendSecret(gc.ctx, pk); err != nil { if !errcode.Is(err, errcode.ErrCode_ErrGroupSecretAlreadySentToMember) { gc.logger.Info("secret already sent secret to member", logutil.PrivateString("memberpk", base64.StdEncoding.EncodeToString(rawPK))) continue } } else { gc.logger.Info("sent secret to existing member", logutil.PrivateString("memberpk", base64.StdEncoding.EncodeToString(rawPK))) } } } func (gc *GroupContext) TagGroupContextPeers(ipfsCoreAPI ipfsutil.ExtendedCoreAPI, weight int) { id := gc.Group().GroupIDAsString() chSub1, err := gc.metadataStore.EventBus().Subscribe(new(stores.EventNewPeer)) if err != nil { gc.logger.Warn("unable to subscribe to metadata event new peer") return } chSub2, err := gc.messageStore.EventBus().Subscribe(new(stores.EventNewPeer)) if err != nil { gc.logger.Warn("unable to subscribe to message event new peer") return } go func() { defer chSub1.Close() defer chSub2.Close() for { var e any select { case e = <-chSub1.Out(): case e = <-chSub2.Out(): case <-gc.ctx.Done(): return } evt := e.(stores.EventNewPeer) tag := fmt.Sprintf("grp_%s", id) gc.logger.Debug("new peer of interest", logutil.PrivateStringer("peer", evt.Peer), zap.String("tag", tag), zap.Int("score", weight)) ipfsCoreAPI.ConnMgr().TagPeer(evt.Peer, tag, weight) } }() } func (gc *GroupContext) WaitForDeviceAdded(ctx context.Context, devicePK crypto.PubKey) (found chan struct{}) { gc.muDevicesAdded.Lock() defer gc.muDevicesAdded.Unlock() rawpk, err := devicePK.Raw() if err != nil { gc.logger.Error("unable to get raw public key", zap.Error(err)) return } k := string(rawpk) var ok bool if found, ok = gc.devicesAdded[k]; ok { return } groupPublicKey, err := gc.group.GetPubKey() if err != nil { gc.logger.Error("unable to get group public key", zap.Error(err)) return } found = make(chan struct{}) if gc.secretStore.IsChainKeyKnownForDevice(ctx, groupPublicKey, devicePK) { close(found) return } gc.devicesAdded[k] = found return } func (gc *GroupContext) notifyDeviceAdded(dPK []byte) { gc.muDevicesAdded.Lock() k := string(dPK) if cc, ok := gc.devicesAdded[k]; ok { close(cc) delete(gc.devicesAdded, k) } gc.muDevicesAdded.Unlock() } ================================================ FILE: iface_account.go ================================================ package weshnet import ( "github.com/libp2p/go-libp2p/core/crypto" "berty.tech/weshnet/v2/pkg/protocoltypes" "berty.tech/weshnet/v2/pkg/secretstore" ) type AccountKeys interface { AccountPrivKey() (crypto.PrivKey, error) AccountProofPrivKey() (crypto.PrivKey, error) DevicePrivKey() (crypto.PrivKey, error) ContactGroupPrivKey(pk crypto.PubKey) (crypto.PrivKey, error) MemberDeviceForGroup(g *protocoltypes.Group) (secretstore.OwnMemberDevice, error) } ================================================ FILE: infra/.gitignore ================================================ !.env ================================================ FILE: infra/README.md ================================================ # How to Deploy Weshnet Infrastructure This guide explains how to set up the essential services for a Weshnet network infrastructure, including rendez-vous points, emitter.io, and relay services. ## Prerequisites Before starting the deployment, ensure you have the following tools installed on your system: 1. **Docker**: Required to run all services in containers - For Ubuntu/Debian: `sudo apt update && sudo apt install docker.io docker-compose` - For macOS: Download and install Docker Desktop from [docker.com](https://www.docker.com/products/docker-desktop) - For Windows: Download and install Docker Desktop from [docker.com](https://www.docker.com/products/docker-desktop) 2. **Make**: Used to simplify deployment commands - For Ubuntu/Debian: `sudo apt install make` - For macOS: Install Xcode Command Line Tools with `xcode-select --install` - For Windows: Install via [Chocolatey](https://chocolatey.org/) with `choco install make` Verify installations with `docker --version` and `make --version`. ## Service Overview Weshnet relies on three main components to facilitate peer-to-peer communication: 1. **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. 2. **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. 3. **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. ## Rendez-vous Point and Emitter.io Services ### Setting Up Rendez-vous Point 1. Generate a new private key for the rendez-vous point service: ```sh cd rdvp docker run --rm --entrypoint rdvp bertytech/berty:kubo-v0.29.0 genkey ``` 2. When the command completes, you'll receive a key in this format: `CAESQHW91QjcGJN1RrIXtzCf8aC5EHCIB2Q+CSJ6KI68E7WLn49INScVKtToDjCMk4TxnncKWFcys59TjCgu8yBDOD8=` 3. Copy this key to the `RDVP_PK` variable in your `.env` file. 4. Add your public IP address to the `ANNOUNCE_SERVER` variable in the same file. ### Setting Up Emitter.io 1. Generate a license and secret key for emitter.io: ```sh cd rdvp docker run --rm emitter/server:v3.1 ``` 2. From the output, copy: - The license to the `EMITTER_LICENSE` variable - The secret key to the `EMITTER_SECRET_KEY` variable in your `.env` file ### Starting the Services Once your configuration is complete, start both services with: ```sh make up ``` ### Configuring Your App to Use the Services Print the multiaddress of the rendez-vous point service with: ```sh docker compose logs rdvp | grep maddr ``` For mobile, you can prefer the "quic" multiaddress which looks something like `/ip4/51.15.25.200/udp/4040/quic-v1/p2p/12D3KooWPFQYmKg3KqZkeXyhwTBhpDu1cWNE8VruyxiMiroStNqh` . To configure Berty Messenger, click the user icon to open Settings. Click Network. Click Rendezvous Point Nodes. Click the + to add a node. To configure your Wesh app, create the service client with the `WithP2PRdvpMaddrs` option. For example: ```go client1, err := weshnet.NewPersistentServiceClient("data1", weshnet.WithP2PRdvpMaddrs([]string{ "/ip4/51.15.25.200/udp/4040/quic-v1/p2p/12D3KooWPFQYmKg3KqZkeXyhwTBhpDu1cWNE8VruyxiMiroStNq", })) ``` ## Relay Service The relay service facilitates peer connections through NATs and firewalls. ### Configuration and Deployment 1. Edit the relay configuration file: - Open `relay/config.json` - Update the `Network/AnnounceAddrs` section with your public IP address 2. Deploy your relay: ```sh cd relay make build # Build the relay Docker image make up # Start the relay service ``` ### Configuring Your App to Use the Service Print the multiaddress of the relay service with: ```sh docker compose logs relay | grep -A 3 "Public Addresses" ``` For mobile, you can prefer the "quic" multiaddress which looks something like `/ip4/51.15.25.200/udp/6363/quic/p2p/12D3KooWKjkkYVJg9RtQCiuV8bKheYB5sgVWSpo6LVyoRHtMZXCF` . To configure Berty Messenger, click the user icon to open Settings. Click Network. Click Relay Nodes. Click the + to add a node. To configure your Wesh app, create the service client with the `WithP2PStaticRelays` option. For example: ```go client1, err := weshnet.NewPersistentServiceClient("data1", weshnet.WithP2PStaticRelays([]string{ "/ip4/51.15.25.200/udp/6363/quic/p2p/12D3KooWKjkkYVJg9RtQCiuV8bKheYB5sgVWSpo6LVyoRHtMZXCF", })) ``` ## Verifying Your Deployment After deployment, you can verify your services are running correctly by checking: - Logs for each service - Network connectivity through the announced addresses - Peer connections through your infrastructure ## Troubleshooting If you encounter issues: - Check that ports are properly forwarded on your network - Verify your public IP is correctly configured in all services - Ensure Docker has sufficient resources allocated - Review service logs for specific error messages ================================================ FILE: infra/rdvp/.env ================================================ # Rendez-vous point public key RDVP_PK= # External IP address ANNOUNCE_SERVER= # Emitter.io secret key EMITTER_SECRET_KEY= # Emitter.io license key EMITTER_LICENSE= ================================================ FILE: infra/rdvp/Makefile ================================================ .PHONY: all all: up ps logs up: docker compose up -d logs: docker compose logs --tail=100 -f down ps: docker compose $@ genkey: echo RDVP_PK=`docker compose run server genkey` > .env ip: curl ifconfig.co ================================================ FILE: infra/rdvp/docker-compose.yml ================================================ version: "3.7" services: rdvp: image: bertytech/berty:kubo-v0.29.0 restart: on-failure environment: - RDVP_PK - EMITTER_SECRET_KEY network_mode: bridge entrypoint: rdvp links: - emitter expose: - 8888 ports: - 4040:4040 - 4040:4040/udp command: - serve - "-log.format=json" - "-log.filters=debug+:*" - "--db=:memory:" - "--pk=$RDVP_PK" - "-metrics=:8888" - "-l=/ip4/0.0.0.0/tcp/4040,/ip4/0.0.0.0/udp/4040/quic-v1" - "-announce=/ip4/${ANNOUNCE_SERVER}/tcp/4040,/ip4/${ANNOUNCE_SERVER}/udp/4040/quic-v1" - "-emitter-admin-key=$EMITTER_SECRET_KEY" - "-emitter-public-addr=tcp://${ANNOUNCE_SERVER}:9494" - "-emitter-server=tcp://emitter:9494" emitter: image: emitter/server:v3.1 container_name: emitter restart: unless-stopped network_mode: bridge ports: - 9494:9494 expose: - 9494 # - 4000 # for cluster usage environment: - EMITTER_LICENSE - EMITTER_LISTEN=:9494 ================================================ FILE: infra/relay/Dockerfile ================================================ # builder FROM golang:1.19-alpine3.16 as builder MAINTAINER gfanton <8671905+gfanton@users.noreply.github.com> ARG GIT_REPOS=https://github.com/libp2p/go-libp2p-relay-daemon.git ARG GIT_TAG=v0.3.0 RUN apk add --no-cache git RUN git clone --depth 1 --branch "${GIT_TAG}" "${GIT_REPOS}" /app WORKDIR /app RUN go build -o /go/bin/daemon -v -ldflags="-s -w" -v ./cmd/libp2p-relay-daemon # runner FROM alpine:3.16 COPY --from=builder /go/bin/daemon /usr/local/bin ENTRYPOINT ["/usr/local/bin/daemon"] ================================================ FILE: infra/relay/Makefile ================================================ build: tar -czh . | docker build -t relay - # need to tar because of the symlink .PHONY: build up: docker compose up -d logs: docker compose logs --tail=100 -f down ps: docker compose $@ ================================================ FILE: infra/relay/config.json ================================================ { "RelayV2": { "Enabled": true, "Resources": { "Limit": null } }, "RelayV1": { "Enabled": true, "Resources": { "MaxCircuits": 2048, "MaxCircuitsPerPeer": 128, "BufferSize": 4096 } }, "Network": { "AnnounceAddrs": [ "/ip4//tcp/6363", "/ip4//udp/6363/quic" ], "ListenAddrs": [ "/ip4/0.0.0.0/udp/6363/quic", "/ip6/::/udp/6363/quic", "/ip4/0.0.0.0/tcp/6363", "/ip6/::/tcp/6363" ] } } ================================================ FILE: infra/relay/docker-compose.yml ================================================ version: '3.7' services: relay: image: relay restart: on-failure volumes: - ./config.json:/etc/daemon-config.json - ./data:/etc/daemon command: - -id=/etc/daemon/id.key - -config=/etc/daemon-config.json ports: - 6363:6363 - 6363:6363/udp ================================================ FILE: internal/benchmark/benchmark_test.go ================================================ package benchmark import ( "testing" ) // BenchmarkScenario is a benchmark for the scenario // FIXME: previous benchmark used Berty Messenger dependencies, we need to find a way to benchmark without them func BenchmarkScenario(b *testing.B) { } ================================================ FILE: internal/bertyversion/example_test.go ================================================ package bertyversion_test ================================================ FILE: internal/bertyversion/version.go ================================================ package bertyversion var ( Version = "n/a" VcsRef = "n/a" ) ================================================ FILE: internal/datastoreutil/consts.go ================================================ package datastoreutil const ( NamespaceMessageKeystore = "messages_keystore" ) ================================================ FILE: internal/datastoreutil/datastore_namespaced.go ================================================ package datastoreutil import ( ds "github.com/ipfs/go-datastore" "github.com/ipfs/go-datastore/keytransform" ) type namespacedDatastore struct { ds.Batching } func (n *namespacedDatastore) Close() error { // noop return nil } func NewNamespacedDatastore(child ds.Datastore, prefix ds.Key) ds.Batching { return &namespacedDatastore{Batching: keytransform.Wrap(child, keytransform.PrefixTransform{Prefix: prefix})} } ================================================ FILE: internal/handshake/doc.go ================================================ // Package handshake implements a capability-based handshake. // // Handshake Sequence Diagram: // --------------------------- // Handshake vastely inspired by Scuttlebutt's Capability-based Handshake // https://scuttlebot.io/more/protocols/shs.pdf // // - a, b are ephemeral key pairs generated by respectively Requester and // Responder. Ephemeral keys are used for one handshake only and then // discarded. They guarantee the freshness of the messages and avoid // replay attacks. // // - A, B are the Account IDs of respectively Requester and Responder. // // - a.b denotes a secret derived from the two keys a and b. // // - | is the concatenation operator. // // - box[a.b](content) denotes the encryption of content using Nacl box // with a.b as key. // // - sig[A](content) denotes the signature of content verified by A. // // +-----------+ +-----------+ // | Requester | | Responder | // +-----------+ +-----------+ // | ---------------------\ | // |-| 1. Requester Hello | | // | |--------------------| | // | | // | a | // |---------------------------------->| // | ---------------------\ | // | | 2. Responder Hello |-| // | |--------------------| | // | | // | b | // |<----------------------------------| // | ----------------------------\ | // |-| 3. Requester Authenticate | | // | |---------------------------| | // | | // | box[a.b|a.B](A,sig[A](a.b)) | // |---------------------------------->| // | ----------------------\ | // | | 4. Responder Accept |-| // | |---------------------| | // | | // | box[a.b|A.B](sig[B](a.b)) | // |<----------------------------------| // | ---------------------------\ | // |-| 5. Requester Acknowledge | | // | |--------------------------| | // | | // | ok | // |---------------------------------->| // | | // // See the documentation at https://berty.tech/protocol for more information. package handshake ================================================ FILE: internal/handshake/handshake.go ================================================ package handshake import ( crand "crypto/rand" "encoding/base64" p2pcrypto "github.com/libp2p/go-libp2p/core/crypto" "golang.org/x/crypto/nacl/box" "berty.tech/weshnet/v2/pkg/cryptoutil" "berty.tech/weshnet/v2/pkg/errcode" "berty.tech/weshnet/v2/pkg/protoio" "berty.tech/weshnet/v2/pkg/tyber" ) // Constant nonces var ( nonceRequesterAuthenticate = [cryptoutil.NonceSize]byte{1} nonceResponderAccept = [cryptoutil.NonceSize]byte{2} ) // Common struct and methods type handshakeContext struct { reader protoio.Reader writer protoio.Writer ownAccountID p2pcrypto.PrivKey peerAccountID p2pcrypto.PubKey ownEphemeral *[cryptoutil.KeySize]byte peerEphemeral *[cryptoutil.KeySize]byte sharedEphemeral *[cryptoutil.KeySize]byte } func (hc *handshakeContext) toTyberStepMutator() tyber.StepMutator { return func(s tyber.Step) tyber.Step { if hc == nil { return s } if hc.peerAccountID != nil { if cpkb, err := hc.peerAccountID.Raw(); err == nil { s.Details = append(s.Details, tyber.Detail{Name: "ContactPublicKey", Description: base64.RawURLEncoding.EncodeToString(cpkb)}) } } for key, val := range map[string]*[cryptoutil.KeySize]byte{ "OwnEphemeral": hc.ownEphemeral, "PeerEphemeral": hc.peerEphemeral, "SharedEphemeral": hc.sharedEphemeral, } { if val != nil { s.Details = append(s.Details, tyber.Detail{ Name: key, Description: base64.RawURLEncoding.EncodeToString(val[:]), }) } } return s } } // Generates own Ephemeral key pair and send pub key to peer func (hc *handshakeContext) generateOwnEphemeralAndSendPubKey() error { // Generate own Ephemeral key pair ownEphemeralPub, ownEphemeralPriv, err := box.GenerateKey(crand.Reader) if err != nil { return errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err) } // Set own Ephemeral priv key in Handshake Context hc.ownEphemeral = ownEphemeralPriv // Send own Ephemeral pub key to peer hello := HelloPayload{EphemeralPubKey: ownEphemeralPub[:]} if err := hc.writer.WriteMsg(&hello); err != nil { return errcode.ErrCode_ErrStreamWrite.Wrap(err) } return nil } // Receives peer's Ephemeral pub key func (hc *handshakeContext) receivePeerEphemeralPubKey() error { var err error // Receive peer's Ephemeral pub key hello := HelloPayload{} if err := hc.reader.ReadMsg(&hello); err != nil { return errcode.ErrCode_ErrStreamRead.Wrap(err) } // Set peer's Ephemeral pub key in Handshake Context hc.peerEphemeral, err = cryptoutil.KeySliceToArray(hello.EphemeralPubKey) if err != nil { return errcode.ErrCode_ErrSerialization.Wrap(err) } return nil } // Computes box key for step 3 (Requester Authenticate): box[a.b|a.B] func (hc *handshakeContext) computeRequesterAuthenticateBoxKey(asRequester bool) (*[cryptoutil.KeySize]byte, error) { var sharedReqEphemeralRespAccountID [cryptoutil.KeySize]byte // If this function was called by the requester if asRequester { // Convert Ed25519 peer's AccountID key to X25519 key mongPeerAccountID, err := cryptoutil.EdwardsToMontgomeryPub(hc.peerAccountID) if err != nil { return nil, errcode.ErrCode_ErrCryptoKeyConversion.Wrap(err) } // Compute shared key from own Ephemeral key and peer's AccountID key box.Precompute( &sharedReqEphemeralRespAccountID, mongPeerAccountID, hc.ownEphemeral, ) } else { // Convert Ed25519 own AccountID key to X25519 key mongOwnAccountID, err := cryptoutil.EdwardsToMontgomeryPriv(hc.ownAccountID) if err != nil { return nil, errcode.ErrCode_ErrCryptoKeyConversion.Wrap(err) } // Compute shared key from peer's Ephemeral key and own AccountID key box.Precompute( &sharedReqEphemeralRespAccountID, hc.peerEphemeral, mongOwnAccountID, ) } // Concatenate both shared keys and hash them using sha256 boxKey := cryptoutil.ConcatAndHashSha256( hc.sharedEphemeral[:], sharedReqEphemeralRespAccountID[:], ) return boxKey, nil } // Computes box key for step 4 (Responder Accept): box[a.b|A.B] func (hc *handshakeContext) computeResponderAcceptBoxKey() (*[cryptoutil.KeySize]byte, error) { var sharedAccountID [cryptoutil.KeySize]byte // Convert Ed25519 AccountID keys to X25519 keys mongOwnAccountID, mongPeerAccountID, err := cryptoutil.EdwardsToMontgomery( hc.ownAccountID, hc.peerAccountID, ) if err != nil { return nil, errcode.ErrCode_ErrCryptoKeyConversion.Wrap(err) } // Compute shared key from AccountID keys (X25519 converted) box.Precompute(&sharedAccountID, mongPeerAccountID, mongOwnAccountID) // Concatenate both shared keys and hash them using sha256 boxKey := cryptoutil.ConcatAndHashSha256( hc.sharedEphemeral[:], sharedAccountID[:], ) return boxKey, nil } ================================================ FILE: internal/handshake/handshake.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.34.2 // protoc (unknown) // source: handshake/handshake.proto package handshake import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type BoxEnvelope struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Box []byte `protobuf:"bytes,1,opt,name=box,proto3" json:"box,omitempty"` } func (x *BoxEnvelope) Reset() { *x = BoxEnvelope{} if protoimpl.UnsafeEnabled { mi := &file_handshake_handshake_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *BoxEnvelope) String() string { return protoimpl.X.MessageStringOf(x) } func (*BoxEnvelope) ProtoMessage() {} func (x *BoxEnvelope) ProtoReflect() protoreflect.Message { mi := &file_handshake_handshake_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use BoxEnvelope.ProtoReflect.Descriptor instead. func (*BoxEnvelope) Descriptor() ([]byte, []int) { return file_handshake_handshake_proto_rawDescGZIP(), []int{0} } func (x *BoxEnvelope) GetBox() []byte { if x != nil { return x.Box } return nil } type HelloPayload struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields EphemeralPubKey []byte `protobuf:"bytes,1,opt,name=ephemeral_pub_key,json=ephemeralPubKey,proto3" json:"ephemeral_pub_key,omitempty"` } func (x *HelloPayload) Reset() { *x = HelloPayload{} if protoimpl.UnsafeEnabled { mi := &file_handshake_handshake_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *HelloPayload) String() string { return protoimpl.X.MessageStringOf(x) } func (*HelloPayload) ProtoMessage() {} func (x *HelloPayload) ProtoReflect() protoreflect.Message { mi := &file_handshake_handshake_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use HelloPayload.ProtoReflect.Descriptor instead. func (*HelloPayload) Descriptor() ([]byte, []int) { return file_handshake_handshake_proto_rawDescGZIP(), []int{1} } func (x *HelloPayload) GetEphemeralPubKey() []byte { if x != nil { return x.EphemeralPubKey } return nil } type RequesterAuthenticatePayload struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields RequesterAccountId []byte `protobuf:"bytes,1,opt,name=requester_account_id,json=requesterAccountId,proto3" json:"requester_account_id,omitempty"` RequesterAccountSig []byte `protobuf:"bytes,2,opt,name=requester_account_sig,json=requesterAccountSig,proto3" json:"requester_account_sig,omitempty"` } func (x *RequesterAuthenticatePayload) Reset() { *x = RequesterAuthenticatePayload{} if protoimpl.UnsafeEnabled { mi := &file_handshake_handshake_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *RequesterAuthenticatePayload) String() string { return protoimpl.X.MessageStringOf(x) } func (*RequesterAuthenticatePayload) ProtoMessage() {} func (x *RequesterAuthenticatePayload) ProtoReflect() protoreflect.Message { mi := &file_handshake_handshake_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use RequesterAuthenticatePayload.ProtoReflect.Descriptor instead. func (*RequesterAuthenticatePayload) Descriptor() ([]byte, []int) { return file_handshake_handshake_proto_rawDescGZIP(), []int{2} } func (x *RequesterAuthenticatePayload) GetRequesterAccountId() []byte { if x != nil { return x.RequesterAccountId } return nil } func (x *RequesterAuthenticatePayload) GetRequesterAccountSig() []byte { if x != nil { return x.RequesterAccountSig } return nil } type ResponderAcceptPayload struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields ResponderAccountSig []byte `protobuf:"bytes,1,opt,name=responder_account_sig,json=responderAccountSig,proto3" json:"responder_account_sig,omitempty"` } func (x *ResponderAcceptPayload) Reset() { *x = ResponderAcceptPayload{} if protoimpl.UnsafeEnabled { mi := &file_handshake_handshake_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ResponderAcceptPayload) String() string { return protoimpl.X.MessageStringOf(x) } func (*ResponderAcceptPayload) ProtoMessage() {} func (x *ResponderAcceptPayload) ProtoReflect() protoreflect.Message { mi := &file_handshake_handshake_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ResponderAcceptPayload.ProtoReflect.Descriptor instead. func (*ResponderAcceptPayload) Descriptor() ([]byte, []int) { return file_handshake_handshake_proto_rawDescGZIP(), []int{3} } func (x *ResponderAcceptPayload) GetResponderAccountSig() []byte { if x != nil { return x.ResponderAccountSig } return nil } type RequesterAcknowledgePayload struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` } func (x *RequesterAcknowledgePayload) Reset() { *x = RequesterAcknowledgePayload{} if protoimpl.UnsafeEnabled { mi := &file_handshake_handshake_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *RequesterAcknowledgePayload) String() string { return protoimpl.X.MessageStringOf(x) } func (*RequesterAcknowledgePayload) ProtoMessage() {} func (x *RequesterAcknowledgePayload) ProtoReflect() protoreflect.Message { mi := &file_handshake_handshake_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use RequesterAcknowledgePayload.ProtoReflect.Descriptor instead. func (*RequesterAcknowledgePayload) Descriptor() ([]byte, []int) { return file_handshake_handshake_proto_rawDescGZIP(), []int{4} } func (x *RequesterAcknowledgePayload) GetSuccess() bool { if x != nil { return x.Success } return false } var File_handshake_handshake_proto protoreflect.FileDescriptor var file_handshake_handshake_proto_rawDesc = []byte{ 0x0a, 0x19, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x2f, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x22, 0x1f, 0x0a, 0x0b, 0x42, 0x6f, 0x78, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x62, 0x6f, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x62, 0x6f, 0x78, 0x22, 0x3a, 0x0a, 0x0c, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x2a, 0x0a, 0x11, 0x65, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x5f, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x65, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x22, 0x84, 0x01, 0x0a, 0x1c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x72, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x30, 0x0a, 0x14, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x12, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x32, 0x0a, 0x15, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x73, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x13, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x69, 0x67, 0x22, 0x4c, 0x0a, 0x16, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x64, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x32, 0x0a, 0x15, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x73, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x13, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x64, 0x65, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x69, 0x67, 0x22, 0x37, 0x0a, 0x1b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x72, 0x41, 0x63, 0x6b, 0x6e, 0x6f, 0x77, 0x6c, 0x65, 0x64, 0x67, 0x65, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x42, 0x2a, 0x5a, 0x28, 0x62, 0x65, 0x72, 0x74, 0x79, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x2f, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2f, 0x76, 0x32, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( file_handshake_handshake_proto_rawDescOnce sync.Once file_handshake_handshake_proto_rawDescData = file_handshake_handshake_proto_rawDesc ) func file_handshake_handshake_proto_rawDescGZIP() []byte { file_handshake_handshake_proto_rawDescOnce.Do(func() { file_handshake_handshake_proto_rawDescData = protoimpl.X.CompressGZIP(file_handshake_handshake_proto_rawDescData) }) return file_handshake_handshake_proto_rawDescData } var file_handshake_handshake_proto_msgTypes = make([]protoimpl.MessageInfo, 5) var file_handshake_handshake_proto_goTypes = []any{ (*BoxEnvelope)(nil), // 0: handshake.BoxEnvelope (*HelloPayload)(nil), // 1: handshake.HelloPayload (*RequesterAuthenticatePayload)(nil), // 2: handshake.RequesterAuthenticatePayload (*ResponderAcceptPayload)(nil), // 3: handshake.ResponderAcceptPayload (*RequesterAcknowledgePayload)(nil), // 4: handshake.RequesterAcknowledgePayload } var file_handshake_handshake_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type 0, // [0:0] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_handshake_handshake_proto_init() } func file_handshake_handshake_proto_init() { if File_handshake_handshake_proto != nil { return } if !protoimpl.UnsafeEnabled { file_handshake_handshake_proto_msgTypes[0].Exporter = func(v any, i int) any { switch v := v.(*BoxEnvelope); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_handshake_handshake_proto_msgTypes[1].Exporter = func(v any, i int) any { switch v := v.(*HelloPayload); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_handshake_handshake_proto_msgTypes[2].Exporter = func(v any, i int) any { switch v := v.(*RequesterAuthenticatePayload); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_handshake_handshake_proto_msgTypes[3].Exporter = func(v any, i int) any { switch v := v.(*ResponderAcceptPayload); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_handshake_handshake_proto_msgTypes[4].Exporter = func(v any, i int) any { switch v := v.(*RequesterAcknowledgePayload); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_handshake_handshake_proto_rawDesc, NumEnums: 0, NumMessages: 5, NumExtensions: 0, NumServices: 0, }, GoTypes: file_handshake_handshake_proto_goTypes, DependencyIndexes: file_handshake_handshake_proto_depIdxs, MessageInfos: file_handshake_handshake_proto_msgTypes, }.Build() File_handshake_handshake_proto = out.File file_handshake_handshake_proto_rawDesc = nil file_handshake_handshake_proto_goTypes = nil file_handshake_handshake_proto_depIdxs = nil } ================================================ FILE: internal/handshake/handshake_test.go ================================================ package handshake import ( "context" crand "crypto/rand" "sync" "testing" "time" p2pcrypto "github.com/libp2p/go-libp2p/core/crypto" p2pnetwork "github.com/libp2p/go-libp2p/core/network" "github.com/stretchr/testify/require" "go.uber.org/zap" "golang.org/x/crypto/nacl/box" "google.golang.org/protobuf/proto" "berty.tech/weshnet/v2/pkg/cryptoutil" "berty.tech/weshnet/v2/pkg/errcode" "berty.tech/weshnet/v2/pkg/ipfsutil" "berty.tech/weshnet/v2/pkg/protoio" "berty.tech/weshnet/v2/pkg/testutil" ) // Request init a handshake with the responder func Request(stream p2pnetwork.Stream, ownAccountID p2pcrypto.PrivKey, peerAccountID p2pcrypto.PubKey) error { reader := protoio.NewDelimitedReader(stream, 2048) writer := protoio.NewDelimitedWriter(stream) return RequestUsingReaderWriter(context.TODO(), zap.NewNop(), reader, writer, ownAccountID, peerAccountID) } // Response handle the handshake inited by the requester func Response(stream p2pnetwork.Stream, ownAccountID p2pcrypto.PrivKey) (p2pcrypto.PubKey, error) { reader := protoio.NewDelimitedReader(stream, 2048) writer := protoio.NewDelimitedWriter(stream) return ResponseUsingReaderWriter(context.TODO(), zap.NewNop(), reader, writer, ownAccountID) } func TestValidHandshake(t *testing.T) { testutil.FilterSpeed(t, testutil.Slow) var requesterTest requesterTestFunc = func( t *testing.T, stream p2pnetwork.Stream, mh *mockedHandshake, ) { defer ipfsutil.FullClose(stream) err := Request( stream, mh.requester.accountID, mh.responder.accountID.GetPublic(), ) require.NoError(t, err, "handshake request failed") } var responderTest responderTestFunc = func( t *testing.T, stream p2pnetwork.Stream, mh *mockedHandshake, wg *sync.WaitGroup, ) { defer wg.Done() defer ipfsutil.FullClose(stream) peerAccountID, err := Response(stream, mh.responder.accountID) require.NoError(t, err, "handshake response failed") require.True( t, peerAccountID.Equals(mh.requester.accountID.GetPublic()), "received peerAccountID by responder != requester's AccountID", ) } runHandshakeTest(t, requesterTest, responderTest) } func TestInvalidRequesterHello(t *testing.T) { testutil.FilterSpeed(t, testutil.Slow) t.Log("Requester interrupts by closing stream") { start := time.Now() var requesterTest requesterTestFunc = func( _ *testing.T, stream p2pnetwork.Stream, _ *mockedHandshake, ) { ipfsutil.FullClose(stream) } var responderTest responderTestFunc = func( t *testing.T, stream p2pnetwork.Stream, mh *mockedHandshake, wg *sync.WaitGroup, ) { defer wg.Done() defer ipfsutil.FullClose(stream) _, err := Response(stream, mh.responder.accountID) require.Contains(t, errcode.Codes(err), errcode.ErrCode_ErrHandshakeRequesterHello) require.Contains(t, errcode.Codes(err), errcode.ErrCode_ErrStreamRead) } runHandshakeTest(t, requesterTest, responderTest) t.Logf("\tduration: %s", time.Since(start)) } } func TestInvalidResponderHello(t *testing.T) { testutil.FilterSpeed(t, testutil.Slow) t.Log("Responder interrupts by closing stream") { start := time.Now() var requesterTest requesterTestFunc = func( t *testing.T, stream p2pnetwork.Stream, mh *mockedHandshake, ) { defer ipfsutil.FullClose(stream) err := Request( stream, mh.requester.accountID, mh.responder.accountID.GetPublic(), ) require.Equal(t, errcode.Codes(err), []errcode.ErrCode{errcode.ErrCode_ErrHandshakeResponderHello, errcode.ErrCode_ErrHandshakePeerEphemeralKeyRecv, errcode.ErrCode_ErrStreamRead}) } var responderTest responderTestFunc = func( t *testing.T, stream p2pnetwork.Stream, mh *mockedHandshake, wg *sync.WaitGroup, ) { defer wg.Done() hc := newTestHandshakeContext(stream, mh.responder.accountID, nil) err := hc.receiveRequesterHello() require.NoError(t, err, "receive RequesterHello failed") ipfsutil.FullClose(stream) } runHandshakeTest(t, requesterTest, responderTest) t.Logf("\tduration: %s", time.Since(start)) } } func TestInvalidRequesterAuthenticate(t *testing.T) { testutil.FilterSpeed(t, testutil.Slow) t.Log("Requester interrupts by closing stream") { start := time.Now() var requesterTest requesterTestFunc = func( t *testing.T, stream p2pnetwork.Stream, mh *mockedHandshake, ) { hc := newTestHandshakeContext( stream, mh.requester.accountID, mh.responder.accountID.GetPublic(), ) err := hc.sendRequesterHello() require.NoError(t, err, "send RequesterHello failed") err = hc.receiveResponderHello() require.NoError(t, err, "receive ResponderHello failed") // Interrupt step by closing stream ipfsutil.FullClose(stream) } var responderTest responderTestFunc = func( t *testing.T, stream p2pnetwork.Stream, mh *mockedHandshake, wg *sync.WaitGroup, ) { defer wg.Done() defer ipfsutil.FullClose(stream) _, err := Response(stream, mh.requester.accountID) require.Equal(t, errcode.Codes(err), []errcode.ErrCode{errcode.ErrCode_ErrHandshakeRequesterAuthenticate, errcode.ErrCode_ErrStreamRead}) } runHandshakeTest(t, requesterTest, responderTest) t.Logf("\tduration: %s", time.Since(start)) } t.Log("Requester sends invalid AccountID") { start := time.Now() var requesterTest requesterTestFunc = func( t *testing.T, stream p2pnetwork.Stream, mh *mockedHandshake, ) { var request RequesterAuthenticatePayload hc := newTestHandshakeContext( stream, mh.requester.accountID, mh.responder.accountID.GetPublic(), ) err := hc.sendRequesterHello() require.NoError(t, err, "send RequesterHello failed") err = hc.receiveResponderHello() require.NoError(t, err, "receive ResponderHello failed") // Send invalid AccountID request.RequesterAccountId = []byte("NotAKey") request.RequesterAccountSig, err = hc.ownAccountID.Sign(hc.sharedEphemeral[:]) require.NoError(t, err, "sharedEphemeral signing failed") requestBytes, err := proto.Marshal(&request) require.NoError(t, err, "request marshaling failed") boxKey, err := hc.computeRequesterAuthenticateBoxKey(true) require.NoError(t, err, "Requester Authenticate box key gen failed") boxContent := box.SealAfterPrecomputation( nil, requestBytes, &nonceRequesterAuthenticate, boxKey, ) hc.writer.WriteMsg(&BoxEnvelope{Box: boxContent}) ipfsutil.FullClose(stream) } var responderTest responderTestFunc = func( t *testing.T, stream p2pnetwork.Stream, mh *mockedHandshake, wg *sync.WaitGroup, ) { defer wg.Done() defer ipfsutil.FullClose(stream) _, err := Response(stream, mh.responder.accountID) require.Equal(t, errcode.Codes(err), []errcode.ErrCode{errcode.ErrCode_ErrHandshakeRequesterAuthenticate, errcode.ErrCode_ErrDeserialization}) } runHandshakeTest(t, requesterTest, responderTest) t.Logf("\tduration: %s", time.Since(start)) } t.Log("Requester sends another AccountID") { start := time.Now() var requesterTest requesterTestFunc = func( t *testing.T, stream p2pnetwork.Stream, mh *mockedHandshake, ) { var request RequesterAuthenticatePayload hc := newTestHandshakeContext( stream, mh.requester.accountID, mh.responder.accountID.GetPublic(), ) err := hc.sendRequesterHello() require.NoError(t, err, "send RequesterHello failed") err = hc.receiveResponderHello() require.NoError(t, err, "receive ResponderHello failed") // Send another AccountID _, wrongAccountIDPub, err := p2pcrypto.GenerateEd25519Key(crand.Reader) require.NoError(t, err, "wrongAccountID generation failed") request.RequesterAccountId, err = p2pcrypto.MarshalPublicKey(wrongAccountIDPub) require.NoError(t, err, "wrongAccountID marshaling failed") request.RequesterAccountSig, err = hc.ownAccountID.Sign(hc.sharedEphemeral[:]) require.NoError(t, err, "sharedEphemeral signing failed") requestBytes, err := proto.Marshal(&request) require.NoError(t, err, "request marshaling failed") boxKey, err := hc.computeRequesterAuthenticateBoxKey(true) require.NoError(t, err, "Requester Authenticate box key gen failed") boxContent := box.SealAfterPrecomputation( nil, requestBytes, &nonceRequesterAuthenticate, boxKey, ) hc.writer.WriteMsg(&BoxEnvelope{Box: boxContent}) ipfsutil.FullClose(stream) } var responderTest responderTestFunc = func( t *testing.T, stream p2pnetwork.Stream, mh *mockedHandshake, wg *sync.WaitGroup, ) { defer wg.Done() defer ipfsutil.FullClose(stream) _, err := Response(stream, mh.responder.accountID) require.Equal(t, errcode.Codes(err), []errcode.ErrCode{errcode.ErrCode_ErrHandshakeRequesterAuthenticate, errcode.ErrCode_ErrCryptoSignatureVerification}) } runHandshakeTest(t, requesterTest, responderTest) t.Logf("\tduration: %s", time.Since(start)) } t.Log("Requester signs with another AccountID") { start := time.Now() var requesterTest requesterTestFunc = func( t *testing.T, stream p2pnetwork.Stream, mh *mockedHandshake, ) { var request RequesterAuthenticatePayload hc := newTestHandshakeContext( stream, mh.requester.accountID, mh.responder.accountID.GetPublic(), ) err := hc.sendRequesterHello() require.NoError(t, err, "send RequesterHello failed") err = hc.receiveResponderHello() require.NoError(t, err, "receive ResponderHello failed") request.RequesterAccountId, err = p2pcrypto.MarshalPublicKey(hc.ownAccountID.GetPublic()) require.NoError(t, err, "ownAccountID marshaling failed") // Sign with another AccountID wrongAccountID, _, err := p2pcrypto.GenerateEd25519Key(crand.Reader) require.NoError(t, err, "wrongAccountID generation failed") request.RequesterAccountSig, err = wrongAccountID.Sign(hc.sharedEphemeral[:]) require.NoError(t, err, "sharedEphemeral signing failed") requestBytes, err := proto.Marshal(&request) require.NoError(t, err, "request marshaling failed") boxKey, err := hc.computeRequesterAuthenticateBoxKey(true) require.NoError(t, err, "Requester Authenticate box key gen failed") boxContent := box.SealAfterPrecomputation( nil, requestBytes, &nonceRequesterAuthenticate, boxKey, ) hc.writer.WriteMsg(&BoxEnvelope{Box: boxContent}) ipfsutil.FullClose(stream) } var responderTest responderTestFunc = func( t *testing.T, stream p2pnetwork.Stream, mh *mockedHandshake, wg *sync.WaitGroup, ) { defer wg.Done() defer ipfsutil.FullClose(stream) _, err := Response(stream, mh.responder.accountID) require.Equal(t, errcode.Codes(err), []errcode.ErrCode{errcode.ErrCode_ErrHandshakeRequesterAuthenticate, errcode.ErrCode_ErrCryptoSignatureVerification}) } runHandshakeTest(t, requesterTest, responderTest) t.Logf("\tduration: %s", time.Since(start)) } t.Log("Requester signs invalid proof") { start := time.Now() var requesterTest requesterTestFunc = func( t *testing.T, stream p2pnetwork.Stream, mh *mockedHandshake, ) { var request RequesterAuthenticatePayload hc := newTestHandshakeContext( stream, mh.requester.accountID, mh.responder.accountID.GetPublic(), ) err := hc.sendRequesterHello() require.NoError(t, err, "send RequesterHello failed") err = hc.receiveResponderHello() require.NoError(t, err, "receive ResponderHello failed") request.RequesterAccountId, err = p2pcrypto.MarshalPublicKey(hc.ownAccountID.GetPublic()) require.NoError(t, err, "ownAccountID marshaling failed") // Sign invalid proof request.RequesterAccountSig, err = hc.ownAccountID.Sign([]byte("WrongProof")) require.NoError(t, err, "sharedEphemeral signing failed") requestBytes, err := proto.Marshal(&request) require.NoError(t, err, "request marshaling failed") boxKey, err := hc.computeRequesterAuthenticateBoxKey(true) require.NoError(t, err, "Requester Authenticate box key gen failed") boxContent := box.SealAfterPrecomputation( nil, requestBytes, &nonceRequesterAuthenticate, boxKey, ) hc.writer.WriteMsg(&BoxEnvelope{Box: boxContent}) ipfsutil.FullClose(stream) } var responderTest responderTestFunc = func( t *testing.T, stream p2pnetwork.Stream, mh *mockedHandshake, wg *sync.WaitGroup, ) { defer wg.Done() defer ipfsutil.FullClose(stream) _, err := Response(stream, mh.responder.accountID) require.Equal(t, errcode.Codes(err), []errcode.ErrCode{errcode.ErrCode_ErrHandshakeRequesterAuthenticate, errcode.ErrCode_ErrCryptoSignatureVerification}) } runHandshakeTest(t, requesterTest, responderTest) t.Logf("\tduration: %s", time.Since(start)) } t.Log("Requester sends invalid request content") { start := time.Now() var requesterTest requesterTestFunc = func( t *testing.T, stream p2pnetwork.Stream, mh *mockedHandshake, ) { hc := newTestHandshakeContext( stream, mh.requester.accountID, mh.responder.accountID.GetPublic(), ) err := hc.sendRequesterHello() require.NoError(t, err, "send RequesterHello failed") err = hc.receiveResponderHello() require.NoError(t, err, "receive ResponderHello failed") // Send invalid request content requestBytes := []byte("WrongRequestContent") boxKey, err := hc.computeRequesterAuthenticateBoxKey(true) require.NoError(t, err, "Requester Authenticate box key gen failed") boxContent := box.SealAfterPrecomputation( nil, requestBytes, &nonceRequesterAuthenticate, boxKey, ) hc.writer.WriteMsg(&BoxEnvelope{Box: boxContent}) ipfsutil.FullClose(stream) } var responderTest responderTestFunc = func( t *testing.T, stream p2pnetwork.Stream, mh *mockedHandshake, wg *sync.WaitGroup, ) { defer wg.Done() defer ipfsutil.FullClose(stream) _, err := Response(stream, mh.responder.accountID) require.Equal(t, errcode.Codes(err), []errcode.ErrCode{errcode.ErrCode_ErrHandshakeRequesterAuthenticate, errcode.ErrCode_ErrDeserialization}) } runHandshakeTest(t, requesterTest, responderTest) t.Logf("\tduration: %s", time.Since(start)) } t.Log("Requester seals box using another key") { start := time.Now() var requesterTest requesterTestFunc = func( t *testing.T, stream p2pnetwork.Stream, mh *mockedHandshake, ) { var request RequesterAuthenticatePayload hc := newTestHandshakeContext( stream, mh.requester.accountID, mh.responder.accountID.GetPublic(), ) err := hc.sendRequesterHello() require.NoError(t, err, "send RequesterHello failed") err = hc.receiveResponderHello() require.NoError(t, err, "receive ResponderHello failed") request.RequesterAccountId, err = p2pcrypto.MarshalPublicKey(hc.ownAccountID.GetPublic()) require.NoError(t, err, "ownAccountID marshaling failed") request.RequesterAccountSig, err = hc.ownAccountID.Sign(hc.sharedEphemeral[:]) require.NoError(t, err, "sharedEphemeral signing failed") requestBytes, err := proto.Marshal(&request) require.NoError(t, err, "request marshaling failed") // Seal box using another key wrongBoxKey := &[32]byte{} crand.Read(wrongBoxKey[:]) boxContent := box.SealAfterPrecomputation( nil, requestBytes, &nonceRequesterAuthenticate, wrongBoxKey, ) hc.writer.WriteMsg(&BoxEnvelope{Box: boxContent}) ipfsutil.FullClose(stream) } var responderTest responderTestFunc = func( t *testing.T, stream p2pnetwork.Stream, mh *mockedHandshake, wg *sync.WaitGroup, ) { defer wg.Done() defer ipfsutil.FullClose(stream) _, err := Response(stream, mh.responder.accountID) require.Equal(t, errcode.Codes(err), []errcode.ErrCode{errcode.ErrCode_ErrHandshakeRequesterAuthenticate, errcode.ErrCode_ErrCryptoDecrypt}) } runHandshakeTest(t, requesterTest, responderTest) t.Logf("\tduration: %s", time.Since(start)) } t.Log("Requester seals using another nonce") { start := time.Now() var requesterTest requesterTestFunc = func( t *testing.T, stream p2pnetwork.Stream, mh *mockedHandshake, ) { var request RequesterAuthenticatePayload hc := newTestHandshakeContext( stream, mh.requester.accountID, mh.responder.accountID.GetPublic(), ) err := hc.sendRequesterHello() require.NoError(t, err, "send RequesterHello failed") err = hc.receiveResponderHello() require.NoError(t, err, "receive ResponderHello failed") request.RequesterAccountId, err = p2pcrypto.MarshalPublicKey(hc.ownAccountID.GetPublic()) require.NoError(t, err, "ownAccountID marshaling failed") request.RequesterAccountSig, err = hc.ownAccountID.Sign(hc.sharedEphemeral[:]) require.NoError(t, err, "sharedEphemeral signing failed") requestBytes, err := proto.Marshal(&request) require.NoError(t, err, "request marshaling failed") boxKey, err := hc.computeRequesterAuthenticateBoxKey(true) require.NoError(t, err, "Requester Authenticate box key gen failed") // Seals using another nonce wrongNonce, err := cryptoutil.GenerateNonce() require.NoError(t, err, "nonce generation failed") boxContent := box.SealAfterPrecomputation( nil, requestBytes, wrongNonce, boxKey, ) hc.writer.WriteMsg(&BoxEnvelope{Box: boxContent}) ipfsutil.FullClose(stream) } var responderTest responderTestFunc = func( t *testing.T, stream p2pnetwork.Stream, mh *mockedHandshake, wg *sync.WaitGroup, ) { defer wg.Done() defer ipfsutil.FullClose(stream) _, err := Response(stream, mh.responder.accountID) require.Equal(t, errcode.Codes(err), []errcode.ErrCode{errcode.ErrCode_ErrHandshakeRequesterAuthenticate, errcode.ErrCode_ErrCryptoDecrypt}) } runHandshakeTest(t, requesterTest, responderTest) t.Logf("\tduration: %s", time.Since(start)) } t.Log("Requester sends invalid box content") { start := time.Now() var requesterTest requesterTestFunc = func( t *testing.T, stream p2pnetwork.Stream, mh *mockedHandshake, ) { hc := newTestHandshakeContext( stream, mh.requester.accountID, mh.responder.accountID.GetPublic(), ) err := hc.sendRequesterHello() require.NoError(t, err, "send RequesterHello failed") err = hc.receiveResponderHello() require.NoError(t, err, "receive ResponderHello failed") // Send invalid box content hc.writer.WriteMsg(&BoxEnvelope{Box: []byte("WrongBoxContent")}) ipfsutil.FullClose(stream) } var responderTest responderTestFunc = func( t *testing.T, stream p2pnetwork.Stream, mh *mockedHandshake, wg *sync.WaitGroup, ) { defer wg.Done() defer ipfsutil.FullClose(stream) _, err := Response(stream, mh.responder.accountID) require.Equal(t, errcode.Codes(err), []errcode.ErrCode{errcode.ErrCode_ErrHandshakeRequesterAuthenticate, errcode.ErrCode_ErrCryptoDecrypt}) } runHandshakeTest(t, requesterTest, responderTest) t.Logf("\tduration: %s", time.Since(start)) } } func TestInvalidResponderAccept(t *testing.T) { testutil.FilterSpeed(t, testutil.Slow) t.Log("Responder interrupts by closing stream") { start := time.Now() var requesterTest requesterTestFunc = func( t *testing.T, stream p2pnetwork.Stream, mh *mockedHandshake, ) { defer ipfsutil.FullClose(stream) err := Request( stream, mh.requester.accountID, mh.responder.accountID.GetPublic(), ) require.Equal(t, errcode.Codes(err), []errcode.ErrCode{errcode.ErrCode_ErrHandshakeResponderAccept, errcode.ErrCode_ErrStreamRead}) } var responderTest responderTestFunc = func( t *testing.T, stream p2pnetwork.Stream, mh *mockedHandshake, wg *sync.WaitGroup, ) { defer wg.Done() hc := newTestHandshakeContext(stream, mh.responder.accountID, nil) err := hc.receiveRequesterHello() require.NoError(t, err, "receive RequesterHello failed") err = hc.sendResponderHello() require.NoError(t, err, "send ResponderHello failed") err = hc.receiveRequesterAuthenticate() require.NoError(t, err, "receive RequesterAuthenticate failed") ipfsutil.FullClose(stream) } runHandshakeTest(t, requesterTest, responderTest) t.Logf("\tduration: %s", time.Since(start)) } t.Log("Responder signs with another AccountID") { start := time.Now() var requesterTest requesterTestFunc = func( t *testing.T, stream p2pnetwork.Stream, mh *mockedHandshake, ) { defer ipfsutil.FullClose(stream) err := Request( stream, mh.requester.accountID, mh.responder.accountID.GetPublic(), ) require.Equal(t, errcode.Codes(err), []errcode.ErrCode{errcode.ErrCode_ErrHandshakeResponderAccept, errcode.ErrCode_ErrCryptoSignatureVerification}) } var responderTest responderTestFunc = func( t *testing.T, stream p2pnetwork.Stream, mh *mockedHandshake, wg *sync.WaitGroup, ) { var response ResponderAcceptPayload defer wg.Done() hc := newTestHandshakeContext(stream, mh.responder.accountID, nil) err := hc.receiveRequesterHello() require.NoError(t, err, "receive RequesterHello failed") err = hc.sendResponderHello() require.NoError(t, err, "send ResponderHello failed") err = hc.receiveRequesterAuthenticate() require.NoError(t, err, "receive RequesterAuthenticate failed") // Sign with another AccountID wrongAccountID, _, err := p2pcrypto.GenerateEd25519Key(crand.Reader) require.NoError(t, err, "wrongAccountID generation failed") response.ResponderAccountSig, err = wrongAccountID.Sign(hc.sharedEphemeral[:]) require.NoError(t, err, "sharedEphemeral signing failed") responseBytes, err := proto.Marshal(&response) require.NoError(t, err, "response marshaling failed") boxKey, err := hc.computeResponderAcceptBoxKey() require.NoError(t, err, "ResponderAccept Accept box key gen failed") boxContent := box.SealAfterPrecomputation( nil, responseBytes, &nonceResponderAccept, boxKey, ) hc.writer.WriteMsg(&BoxEnvelope{Box: boxContent}) ipfsutil.FullClose(stream) } runHandshakeTest(t, requesterTest, responderTest) t.Logf("\tduration: %s", time.Since(start)) } t.Log("Responder signs invalid proof") { start := time.Now() var requesterTest requesterTestFunc = func( t *testing.T, stream p2pnetwork.Stream, mh *mockedHandshake, ) { defer ipfsutil.FullClose(stream) err := Request( stream, mh.requester.accountID, mh.responder.accountID.GetPublic(), ) require.Equal(t, errcode.Codes(err), []errcode.ErrCode{errcode.ErrCode_ErrHandshakeResponderAccept, errcode.ErrCode_ErrCryptoSignatureVerification}) } var responderTest responderTestFunc = func( t *testing.T, stream p2pnetwork.Stream, mh *mockedHandshake, wg *sync.WaitGroup, ) { var response ResponderAcceptPayload defer wg.Done() hc := newTestHandshakeContext(stream, mh.responder.accountID, nil) err := hc.receiveRequesterHello() require.NoError(t, err, "receive RequesterHello failed") err = hc.sendResponderHello() require.NoError(t, err, "send ResponderHello failed") err = hc.receiveRequesterAuthenticate() require.NoError(t, err, "receive RequesterAuthenticate failed") // Sign invalid proof response.ResponderAccountSig, err = hc.ownAccountID.Sign([]byte("WrongProof")) require.NoError(t, err, "sharedEphemeral signing failed") responseBytes, err := proto.Marshal(&response) require.NoError(t, err, "response marshaling failed") boxKey, err := hc.computeResponderAcceptBoxKey() require.NoError(t, err, "ResponderAccept Accept box key gen failed") boxContent := box.SealAfterPrecomputation( nil, responseBytes, &nonceResponderAccept, boxKey, ) hc.writer.WriteMsg(&BoxEnvelope{Box: boxContent}) ipfsutil.FullClose(stream) } runHandshakeTest(t, requesterTest, responderTest) t.Logf("\tduration: %s", time.Since(start)) } t.Log("Responder sends invalid response content") { start := time.Now() var requesterTest requesterTestFunc = func( t *testing.T, stream p2pnetwork.Stream, mh *mockedHandshake, ) { defer ipfsutil.FullClose(stream) err := Request( stream, mh.requester.accountID, mh.responder.accountID.GetPublic(), ) require.Equal(t, errcode.Codes(err), []errcode.ErrCode{errcode.ErrCode_ErrHandshakeResponderAccept, errcode.ErrCode_ErrDeserialization}) } var responderTest responderTestFunc = func( t *testing.T, stream p2pnetwork.Stream, mh *mockedHandshake, wg *sync.WaitGroup, ) { defer wg.Done() hc := newTestHandshakeContext(stream, mh.responder.accountID, nil) err := hc.receiveRequesterHello() require.NoError(t, err, "receive RequesterHello failed") err = hc.sendResponderHello() require.NoError(t, err, "send ResponderHello failed") err = hc.receiveRequesterAuthenticate() require.NoError(t, err, "receive RequesterAuthenticate failed") // Send invalid response content responseBytes := []byte("WrongResponseContent") boxKey, err := hc.computeResponderAcceptBoxKey() require.NoError(t, err, "ResponderAccept Accept box key gen failed") boxContent := box.SealAfterPrecomputation( nil, responseBytes, &nonceResponderAccept, boxKey, ) hc.writer.WriteMsg(&BoxEnvelope{Box: boxContent}) ipfsutil.FullClose(stream) } runHandshakeTest(t, requesterTest, responderTest) t.Logf("\tduration: %s", time.Since(start)) } t.Log("Responder seals box using another key") { start := time.Now() var requesterTest requesterTestFunc = func( t *testing.T, stream p2pnetwork.Stream, mh *mockedHandshake, ) { defer ipfsutil.FullClose(stream) err := Request( stream, mh.requester.accountID, mh.responder.accountID.GetPublic(), ) require.Equal(t, errcode.Codes(err), []errcode.ErrCode{errcode.ErrCode_ErrHandshakeResponderAccept, errcode.ErrCode_ErrCryptoDecrypt}) } var responderTest responderTestFunc = func( t *testing.T, stream p2pnetwork.Stream, mh *mockedHandshake, wg *sync.WaitGroup, ) { var response ResponderAcceptPayload defer wg.Done() hc := newTestHandshakeContext(stream, mh.responder.accountID, nil) err := hc.receiveRequesterHello() require.NoError(t, err, "receive RequesterHello failed") err = hc.sendResponderHello() require.NoError(t, err, "send ResponderHello failed") err = hc.receiveRequesterAuthenticate() require.NoError(t, err, "receive RequesterAuthenticate failed") response.ResponderAccountSig, err = hc.ownAccountID.Sign(hc.sharedEphemeral[:]) require.NoError(t, err, "sharedEphemeral signing failed") responseBytes, err := proto.Marshal(&response) require.NoError(t, err, "response marshaling failed") // Seal box using another key wrongBoxKey := &[32]byte{} crand.Read(wrongBoxKey[:]) boxContent := box.SealAfterPrecomputation( nil, responseBytes, &nonceResponderAccept, wrongBoxKey, ) hc.writer.WriteMsg(&BoxEnvelope{Box: boxContent}) ipfsutil.FullClose(stream) } runHandshakeTest(t, requesterTest, responderTest) t.Logf("\tduration: %s", time.Since(start)) } t.Log("Responder seals using another nonce") { start := time.Now() var requesterTest requesterTestFunc = func( t *testing.T, stream p2pnetwork.Stream, mh *mockedHandshake, ) { defer ipfsutil.FullClose(stream) err := Request( stream, mh.requester.accountID, mh.responder.accountID.GetPublic(), ) require.Equal(t, errcode.Codes(err), []errcode.ErrCode{errcode.ErrCode_ErrHandshakeResponderAccept, errcode.ErrCode_ErrCryptoDecrypt}) } var responderTest responderTestFunc = func( t *testing.T, stream p2pnetwork.Stream, mh *mockedHandshake, wg *sync.WaitGroup, ) { var response ResponderAcceptPayload defer wg.Done() hc := newTestHandshakeContext(stream, mh.responder.accountID, nil) err := hc.receiveRequesterHello() require.NoError(t, err, "receive RequesterHello failed") err = hc.sendResponderHello() require.NoError(t, err, "send ResponderHello failed") err = hc.receiveRequesterAuthenticate() require.NoError(t, err, "receive RequesterAuthenticate failed") response.ResponderAccountSig, err = hc.ownAccountID.Sign(hc.sharedEphemeral[:]) require.NoError(t, err, "sharedEphemeral signing failed") responseBytes, err := proto.Marshal(&response) require.NoError(t, err, "response marshaling failed") boxKey, err := hc.computeResponderAcceptBoxKey() require.NoError(t, err, "ResponderAccept Accept box key gen failed") // Seals using another nonce wrongNonce, err := cryptoutil.GenerateNonce() require.NoError(t, err, "nonce generation failed") boxContent := box.SealAfterPrecomputation( nil, responseBytes, wrongNonce, boxKey, ) hc.writer.WriteMsg(&BoxEnvelope{Box: boxContent}) ipfsutil.FullClose(stream) } runHandshakeTest(t, requesterTest, responderTest) t.Logf("\tduration: %s", time.Since(start)) } t.Log("Responder sends invalid box content") { start := time.Now() var requesterTest requesterTestFunc = func( t *testing.T, stream p2pnetwork.Stream, mh *mockedHandshake, ) { defer ipfsutil.FullClose(stream) err := Request( stream, mh.requester.accountID, mh.responder.accountID.GetPublic(), ) require.Equal(t, errcode.Codes(err), []errcode.ErrCode{errcode.ErrCode_ErrHandshakeResponderAccept, errcode.ErrCode_ErrCryptoDecrypt}) } var responderTest responderTestFunc = func( t *testing.T, stream p2pnetwork.Stream, mh *mockedHandshake, wg *sync.WaitGroup, ) { defer wg.Done() hc := newTestHandshakeContext(stream, mh.responder.accountID, nil) err := hc.receiveRequesterHello() require.NoError(t, err, "receive RequesterHello failed") err = hc.sendResponderHello() require.NoError(t, err, "send ResponderHello failed") err = hc.receiveRequesterAuthenticate() require.NoError(t, err, "receive RequesterAuthenticate failed") // Send wrong boxContent hc.writer.WriteMsg(&BoxEnvelope{Box: []byte("WrongBoxContent")}) ipfsutil.FullClose(stream) } runHandshakeTest(t, requesterTest, responderTest) t.Logf("\tduration: %s", time.Since(start)) } } func TestInvalidResponderAcceptAck(t *testing.T) { testutil.FilterSpeed(t, testutil.Slow) t.Log("Requester interrupts by closing stream") { start := time.Now() var requesterTest requesterTestFunc = func( t *testing.T, stream p2pnetwork.Stream, mh *mockedHandshake, ) { hc := newTestHandshakeContext( stream, mh.requester.accountID, mh.responder.accountID.GetPublic(), ) err := hc.sendRequesterHello() require.NoError(t, err, "send RequesterHello failed") err = hc.receiveResponderHello() require.NoError(t, err, "receive ResponderHello failed") err = hc.sendRequesterAuthenticate() require.NoError(t, err, "send RequesterAuthenticate failed") err = hc.receiveResponderAccept() require.NoError(t, err, "receive ResponderAccept failed") ipfsutil.FullClose(stream) } var responderTest responderTestFunc = func( t *testing.T, stream p2pnetwork.Stream, mh *mockedHandshake, wg *sync.WaitGroup, ) { defer wg.Done() defer ipfsutil.FullClose(stream) _, err := Response(stream, mh.responder.accountID) require.Equal(t, errcode.Codes(err), []errcode.ErrCode{errcode.ErrCode_ErrHandshakeRequesterAcknowledge, errcode.ErrCode_ErrStreamRead}) } runHandshakeTest(t, requesterTest, responderTest) t.Logf("\tduration: %s", time.Since(start)) } t.Log("Requester sends acknowledge with: success == false") { start := time.Now() var requesterTest requesterTestFunc = func( t *testing.T, stream p2pnetwork.Stream, mh *mockedHandshake, ) { hc := newTestHandshakeContext( stream, mh.requester.accountID, mh.responder.accountID.GetPublic(), ) err := hc.sendRequesterHello() require.NoError(t, err, "send RequesterHello failed") err = hc.receiveResponderHello() require.NoError(t, err, "receive ResponderHello failed") err = hc.sendRequesterAuthenticate() require.NoError(t, err, "send RequesterAuthenticate failed") err = hc.receiveResponderAccept() require.NoError(t, err, "receive ResponderAccept failed") acknowledge := &RequesterAcknowledgePayload{Success: false} hc.writer.WriteMsg(acknowledge) ipfsutil.FullClose(stream) } var responderTest responderTestFunc = func( t *testing.T, stream p2pnetwork.Stream, mh *mockedHandshake, wg *sync.WaitGroup, ) { defer wg.Done() defer ipfsutil.FullClose(stream) _, err := Response(stream, mh.responder.accountID) require.Equal(t, errcode.Codes(err), []errcode.ErrCode{errcode.ErrCode_ErrHandshakeRequesterAcknowledge, errcode.ErrCode_ErrInvalidInput}) } runHandshakeTest(t, requesterTest, responderTest) t.Logf("\tduration: %s", time.Since(start)) } } ================================================ FILE: internal/handshake/handshake_util_test.go ================================================ package handshake import ( "context" crand "crypto/rand" "sync" "testing" "time" p2pcrypto "github.com/libp2p/go-libp2p/core/crypto" p2pnetwork "github.com/libp2p/go-libp2p/core/network" p2ppeer "github.com/libp2p/go-libp2p/core/peer" mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" "github.com/stretchr/testify/require" "berty.tech/weshnet/v2/pkg/ipfsutil" "berty.tech/weshnet/v2/pkg/protoio" "berty.tech/weshnet/v2/pkg/tinder" ) const testProtocolID = "/berty/handshake_test/1.0.0" type mockedPeer struct { accountID p2pcrypto.PrivKey coreAPI ipfsutil.CoreAPIMock peerInfo p2ppeer.AddrInfo } type mockedHandshake struct { requester *mockedPeer responder *mockedPeer } type requesterTestFunc func( t *testing.T, stream p2pnetwork.Stream, mh *mockedHandshake, ) type responderTestFunc func( t *testing.T, stream p2pnetwork.Stream, mh *mockedHandshake, wg *sync.WaitGroup, ) func newMockedPeer(t *testing.T, ctx context.Context, ipfsOpts *ipfsutil.TestingAPIOpts) *mockedPeer { accountID, _, err := p2pcrypto.GenerateEd25519Key(crand.Reader) require.NoError(t, err, "can't create new identity") coreAPI := ipfsutil.TestingCoreAPIUsingMockNet(ctx, t, ipfsOpts) peerInfo := coreAPI.MockNode().Peerstore.PeerInfo(coreAPI.MockNode().Identity) return &mockedPeer{ accountID: accountID, coreAPI: coreAPI, peerInfo: peerInfo, } } func newMockedHandshake(t *testing.T, ctx context.Context) *mockedHandshake { t.Helper() mn := mocknet.New() t.Cleanup(func() { mn.Close() }) opts := &ipfsutil.TestingAPIOpts{ Mocknet: mn, DiscoveryServer: tinder.NewMockDriverServer(), } requester := newMockedPeer(t, ctx, opts) responder := newMockedPeer(t, ctx, opts) // link responder & requester err := opts.Mocknet.LinkAll() require.NoError(t, err, "can't link peers") // connect responder & requester err = opts.Mocknet.ConnectAllButSelf() require.NoError(t, err, "can't connect peers") return &mockedHandshake{ requester: requester, responder: responder, } } func (mh *mockedHandshake) close(t *testing.T) { t.Helper() mh.requester.coreAPI.Close() mh.responder.coreAPI.Close() } func newTestHandshakeContext(stream p2pnetwork.Stream, ownAccountID p2pcrypto.PrivKey, peerAccountID p2pcrypto.PubKey) *handshakeContext { return &handshakeContext{ reader: protoio.NewDelimitedReader(stream, 2048), writer: protoio.NewDelimitedWriter(stream), ownAccountID: ownAccountID, peerAccountID: peerAccountID, sharedEphemeral: &[32]byte{}, } } func runHandshakeTest(t *testing.T, requesterTest requesterTestFunc, responderTest responderTestFunc) { var wg sync.WaitGroup ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() mh := newMockedHandshake(t, ctx) defer mh.close(t) mh.responder.coreAPI.MockNode().PeerHost.SetStreamHandler( testProtocolID, func(stream p2pnetwork.Stream) { wg.Add(1) responderTest(t, stream, mh, &wg) }, ) stream, err := mh.requester.coreAPI.MockNode().PeerHost.NewStream( ctx, mh.responder.peerInfo.ID, testProtocolID, ) require.NoError(t, err, "requester can't dial responder") requesterTest(t, stream, mh) wg.Wait() } ================================================ FILE: internal/handshake/request.go ================================================ package handshake import ( "context" "errors" p2pcrypto "github.com/libp2p/go-libp2p/core/crypto" "go.uber.org/zap" "golang.org/x/crypto/nacl/box" "google.golang.org/protobuf/proto" "berty.tech/weshnet/v2/pkg/cryptoutil" "berty.tech/weshnet/v2/pkg/errcode" "berty.tech/weshnet/v2/pkg/protoio" "berty.tech/weshnet/v2/pkg/tyber" ) // RequestUsingReaderWriter init a handshake with the responder, using provided io reader and writer func RequestUsingReaderWriter(ctx context.Context, logger *zap.Logger, reader protoio.Reader, writer protoio.Writer, ownAccountID p2pcrypto.PrivKey, peerAccountID p2pcrypto.PubKey) error { hc := &handshakeContext{ reader: reader, writer: writer, ownAccountID: ownAccountID, peerAccountID: peerAccountID, sharedEphemeral: &[cryptoutil.KeySize]byte{}, } // Handshake steps on requester side (see comments below) if err := hc.sendRequesterHello(); err != nil { return errcode.ErrCode_ErrHandshakeRequesterHello.Wrap(err) } tyber.LogStep(ctx, logger, "Sent hello", hc.toTyberStepMutator()) if err := hc.receiveResponderHello(); err != nil { return errcode.ErrCode_ErrHandshakeResponderHello.Wrap(err) } tyber.LogStep(ctx, logger, "Received hello", hc.toTyberStepMutator()) if err := hc.sendRequesterAuthenticate(); err != nil { return errcode.ErrCode_ErrHandshakeRequesterAuthenticate.Wrap(err) } tyber.LogStep(ctx, logger, "Sent authenticate", hc.toTyberStepMutator()) if err := hc.receiveResponderAccept(); err != nil { return errcode.ErrCode_ErrHandshakeResponderAccept.Wrap(err) } tyber.LogStep(ctx, logger, "Received accept", hc.toTyberStepMutator()) if err := hc.sendRequesterAcknowledge(); err != nil { return errcode.ErrCode_ErrHandshakeRequesterAcknowledge.Wrap(err) } tyber.LogStep(ctx, logger, "Sent acknowledge", hc.toTyberStepMutator()) return nil } // 1st step - Requester sends: a func (hc *handshakeContext) sendRequesterHello() error { if err := hc.generateOwnEphemeralAndSendPubKey(); err != nil { return errcode.ErrCode_ErrHandshakeOwnEphemeralKeyGenSend.Wrap(err) } return nil } // 2nd step - Requester receives: b func (hc *handshakeContext) receiveResponderHello() error { if err := hc.receivePeerEphemeralPubKey(); err != nil { return errcode.ErrCode_ErrHandshakePeerEphemeralKeyRecv.Wrap(err) } // Compute shared key from Ephemeral keys box.Precompute(hc.sharedEphemeral, hc.peerEphemeral, hc.ownEphemeral) return nil } // 3rd step - Requester sends: box[a.b|a.B](A,sig[A](a.b)) func (hc *handshakeContext) sendRequesterAuthenticate() error { var ( request RequesterAuthenticatePayload err error ) // Set own AccountID pub key and proof (shared_a_b signed by own AccountID) // in RequesterAuthenticatePayload message before marshaling it request.RequesterAccountId, err = p2pcrypto.MarshalPublicKey(hc.ownAccountID.GetPublic()) if err != nil { return errcode.ErrCode_ErrSerialization.Wrap(err) } request.RequesterAccountSig, err = hc.ownAccountID.Sign(hc.sharedEphemeral[:]) if err != nil { return errcode.ErrCode_ErrCryptoSignature.Wrap(err) } requestBytes, err := proto.Marshal(&request) if err != nil { return errcode.ErrCode_ErrSerialization.Wrap(err) } // Compute box key and seal marshaled RequesterAuthenticatePayload using // constant nonce (see handshake.go) boxKey, err := hc.computeRequesterAuthenticateBoxKey(true) if err != nil { return errcode.ErrCode_ErrHandshakeRequesterAuthenticateBoxKeyGen.Wrap(err) } boxContent := box.SealAfterPrecomputation( nil, requestBytes, &nonceRequesterAuthenticate, boxKey, ) // Send BoxEnvelope to responder if err = hc.writer.WriteMsg(&BoxEnvelope{Box: boxContent}); err != nil { return errcode.ErrCode_ErrStreamWrite.Wrap(err) } return nil } // 4th step - Requester receives: box[a.b|A.B](sig[B](a.b)) func (hc *handshakeContext) receiveResponderAccept() error { var ( boxEnvelope BoxEnvelope response ResponderAcceptPayload ) // Receive BoxEnvelope from responder if err := hc.reader.ReadMsg(&boxEnvelope); err != nil { return errcode.ErrCode_ErrStreamRead.Wrap(err) } // Compute box key and open marshaled RequesterAuthenticatePayload using // constant nonce (see handshake.go) boxKey, err := hc.computeResponderAcceptBoxKey() if err != nil { return errcode.ErrCode_ErrHandshakeResponderAcceptBoxKeyGen.Wrap(err) } respBytes, _ := box.OpenAfterPrecomputation( nil, boxEnvelope.Box, &nonceResponderAccept, boxKey, ) if respBytes == nil { err := errors.New("box opening failed") return errcode.ErrCode_ErrCryptoDecrypt.Wrap(err) } // Unmarshal ResponderAcceptPayload err = proto.Unmarshal(respBytes, &response) if err != nil { return errcode.ErrCode_ErrDeserialization.Wrap(err) } // Verify proof (shared_a_b signed by peer's AccountID) valid, err := hc.peerAccountID.Verify( hc.sharedEphemeral[:], response.ResponderAccountSig, ) if err != nil { return errcode.ErrCode_ErrCryptoSignatureVerification.Wrap(err) } else if !valid { return errcode.ErrCode_ErrCryptoSignatureVerification } return nil } // 5th step - Requester sends: ok func (hc *handshakeContext) sendRequesterAcknowledge() error { acknowledge := &RequesterAcknowledgePayload{Success: true} // Send Acknowledge to responder if err := hc.writer.WriteMsg(acknowledge); err != nil { return errcode.ErrCode_ErrStreamWrite.Wrap(err) } return nil } ================================================ FILE: internal/handshake/response.go ================================================ package handshake import ( "context" "errors" p2pcrypto "github.com/libp2p/go-libp2p/core/crypto" "go.uber.org/zap" "golang.org/x/crypto/nacl/box" "google.golang.org/protobuf/proto" "berty.tech/weshnet/v2/pkg/cryptoutil" "berty.tech/weshnet/v2/pkg/errcode" "berty.tech/weshnet/v2/pkg/protoio" "berty.tech/weshnet/v2/pkg/tyber" ) // ResponseUsingReaderWriter handle the handshake inited by the requester, using provided io reader and writer func ResponseUsingReaderWriter(ctx context.Context, logger *zap.Logger, reader protoio.Reader, writer protoio.Writer, ownAccountID p2pcrypto.PrivKey) (p2pcrypto.PubKey, error) { hc := &handshakeContext{ reader: reader, writer: writer, ownAccountID: ownAccountID, sharedEphemeral: &[cryptoutil.KeySize]byte{}, } // Handshake steps on responder side (see comments below) if err := hc.receiveRequesterHello(); err != nil { return nil, errcode.ErrCode_ErrHandshakeRequesterHello.Wrap(err) } tyber.LogStep(ctx, logger, "Received hello", hc.toTyberStepMutator(), tyber.ForceReopen) if err := hc.sendResponderHello(); err != nil { return nil, errcode.ErrCode_ErrHandshakeResponderHello.Wrap(err) } tyber.LogStep(ctx, logger, "Sent hello", hc.toTyberStepMutator(), tyber.ForceReopen) if err := hc.receiveRequesterAuthenticate(); err != nil { return nil, errcode.ErrCode_ErrHandshakeRequesterAuthenticate.Wrap(err) } tyber.LogStep(ctx, logger, "Received authenticate", hc.toTyberStepMutator(), tyber.ForceReopen) if err := hc.sendResponderAccept(); err != nil { return nil, errcode.ErrCode_ErrHandshakeResponderAccept.Wrap(err) } tyber.LogStep(ctx, logger, "Sent accept", hc.toTyberStepMutator(), tyber.ForceReopen) if err := hc.receiveRequesterAcknowledge(); err != nil { return nil, errcode.ErrCode_ErrHandshakeRequesterAcknowledge.Wrap(err) } tyber.LogStep(ctx, logger, "Received acknowledge", hc.toTyberStepMutator(), tyber.ForceReopen) return hc.peerAccountID, nil } // 1st step - Responder receives: a func (hc *handshakeContext) receiveRequesterHello() error { if err := hc.receivePeerEphemeralPubKey(); err != nil { return errcode.ErrCode_ErrHandshakePeerEphemeralKeyRecv.Wrap(err) } return nil } // 2nd step - Responder sends: b func (hc *handshakeContext) sendResponderHello() error { if err := hc.generateOwnEphemeralAndSendPubKey(); err != nil { return errcode.ErrCode_ErrHandshakeOwnEphemeralKeyGenSend.Wrap(err) } // Compute shared key from Ephemeral keys box.Precompute(hc.sharedEphemeral, hc.peerEphemeral, hc.ownEphemeral) return nil } // 3rd step - Responder receives: box[a.b|a.B](A,sig[A](a.b)) func (hc *handshakeContext) receiveRequesterAuthenticate() error { var ( boxEnvelope BoxEnvelope request RequesterAuthenticatePayload ) // Receive BoxEnvelope from requester if err := hc.reader.ReadMsg(&boxEnvelope); err != nil { return errcode.ErrCode_ErrStreamRead.Wrap(err) } // Compute box key and open marshaled RequesterAuthenticatePayload using // constant nonce (see handshake.go) boxKey, err := hc.computeRequesterAuthenticateBoxKey(false) if err != nil { return errcode.ErrCode_ErrHandshakeRequesterAuthenticateBoxKeyGen.Wrap(err) } requestBytes, _ := box.OpenAfterPrecomputation( nil, boxEnvelope.Box, &nonceRequesterAuthenticate, boxKey, ) if requestBytes == nil { err := errors.New("box opening failed") return errcode.ErrCode_ErrCryptoDecrypt.Wrap(err) } // Unmarshal RequesterAuthenticatePayload and RequesterAccountId err = proto.Unmarshal(requestBytes, &request) if err != nil { return errcode.ErrCode_ErrDeserialization.Wrap(err) } hc.peerAccountID, err = p2pcrypto.UnmarshalPublicKey(request.RequesterAccountId) if err != nil { return errcode.ErrCode_ErrDeserialization.Wrap(err) } // Verify proof (shared_a_b signed by peer's AccountID) valid, err := hc.peerAccountID.Verify( hc.sharedEphemeral[:], request.RequesterAccountSig, ) if err != nil { return errcode.ErrCode_ErrCryptoSignatureVerification.Wrap(err) } else if !valid { return errcode.ErrCode_ErrCryptoSignatureVerification } return nil } // 4th step - Responder sends: box[a.b|A.B](sig[B](a.b)) func (hc *handshakeContext) sendResponderAccept() error { var ( response ResponderAcceptPayload err error ) // Set proof (shared_a_b signed by own AccountID) in ResponderAcceptPayload // before marshaling it response.ResponderAccountSig, err = hc.ownAccountID.Sign(hc.sharedEphemeral[:]) if err != nil { return errcode.ErrCode_ErrCryptoSignature.Wrap(err) } responseBytes, err := proto.Marshal(&response) if err != nil { return errcode.ErrCode_ErrSerialization.Wrap(err) } // Compute box key and seal marshaled ResponderAcceptPayload using // constant nonce (see handshake.go) boxKey, err := hc.computeResponderAcceptBoxKey() if err != nil { return errcode.ErrCode_ErrHandshakeResponderAcceptBoxKeyGen.Wrap(err) } boxContent := box.SealAfterPrecomputation( nil, responseBytes, &nonceResponderAccept, boxKey, ) // Send BoxEnvelope to requester if err = hc.writer.WriteMsg(&BoxEnvelope{Box: boxContent}); err != nil { return errcode.ErrCode_ErrStreamWrite.Wrap(err) } return nil } // 5th step - Responder receives: ok func (hc *handshakeContext) receiveRequesterAcknowledge() error { var acknowledge RequesterAcknowledgePayload // Receive Acknowledge from requester if err := hc.reader.ReadMsg(&acknowledge); err != nil { return errcode.ErrCode_ErrStreamRead.Wrap(err) } if !acknowledge.Success { return errcode.ErrCode_ErrInvalidInput } return nil } ================================================ FILE: internal/notify/notify.go ================================================ package notify import ( "context" "sync" ) type Notify struct { L sync.Locker cc chan struct{} mu sync.Mutex } func New(l sync.Locker) *Notify { return &Notify{L: l} } func (n *Notify) getChan() <-chan struct{} { n.mu.Lock() defer n.mu.Unlock() if n.cc == nil { n.cc = make(chan struct{}) } return n.cc } func (n *Notify) Wait(ctx context.Context) (ok bool) { signal := n.getChan() n.L.Unlock() select { case <-ctx.Done(): ok = false case <-signal: ok = true } n.L.Lock() return } func (n *Notify) Broadcast() { n.mu.Lock() if n.cc != nil { close(n.cc) n.cc = nil } n.mu.Unlock() } ================================================ FILE: internal/notify/notify_test.go ================================================ package notify import ( "context" "sync" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestNotify(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() locker := sync.Mutex{} ntfy := New(&locker) locker.Lock() var ok bool = false go func() { locker.Lock() assert.False(t, ok) ok = true ntfy.Broadcast() locker.Unlock() }() ctxok := ntfy.Wait(ctx) assert.True(t, ctxok) assert.True(t, ok) locker.Unlock() } func TestNotifyConcurrentWait(t *testing.T) { const n = 200 ctx, cancel := context.WithCancel(context.Background()) defer cancel() locker := sync.Mutex{} notify := New(&locker) running := make(chan int, n) awake := make(chan int, n) for i := 0; i < n; i++ { go func(g int) { locker.Lock() ok := true for ok { running <- g ok = notify.Wait(ctx) awake <- g } locker.Unlock() }(i) } for i := 0; i < n; i++ { for j := 0; j < n; j++ { <-running // Will deadlock unless n are running. } select { case g := <-awake: require.FailNow(t, "goroutine should be asleep", "goroutine #%d not asleep", g) default: } locker.Lock() if i == n-1 { cancel() } else { notify.Broadcast() } locker.Unlock() seen := make([]bool, n) for j := 0; j < n; j++ { g := <-awake require.Falsef(t, seen[g], "goroutine #%d woke up twice", g) seen[g] = true } } select { case g := <-running: require.FailNow(t, "goroutine should not be running", "goroutine #%d still running", g) default: } } ================================================ FILE: internal/queue/metrics.go ================================================ package queue type MetricsTracer[T any] interface { ItemQueued(name string, item T) ItemPop(name string, item T) } var _ MetricsTracer[any] = (*noopTracer[any])(nil) type noopTracer[T any] struct{} // nolint:revive func (*noopTracer[T]) ItemQueued(name string, item T) {} // nolint:revive func (*noopTracer[T]) ItemPop(name string, item T) {} ================================================ FILE: internal/queue/priority.go ================================================ package queue import ( "container/heap" "sync" ) type ICounter interface { Counter() uint64 } // A priorityMessageQueue implements heap.Interface and holds Items. type PriorityQueue[T ICounter] struct { name string metrics MetricsTracer[T] items []T muMessages sync.RWMutex } func NewPriorityQueue[T ICounter](name string, tracer MetricsTracer[T]) *PriorityQueue[T] { queue := &PriorityQueue[T]{ name: name, metrics: tracer, items: []T{}, } heap.Init(queue) return queue } func (pq *PriorityQueue[T]) Add(m T) { pq.muMessages.Lock() heap.Push(pq, m) pq.metrics.ItemQueued(pq.name, m) pq.muMessages.Unlock() } func (pq *PriorityQueue[T]) NextAll(cb func(next T) error) error { pq.muMessages.Lock() defer pq.muMessages.Unlock() for len(pq.items) > 0 { item := heap.Pop(pq).(T) if err := cb(item); err != nil { return err } } return nil } func (pq *PriorityQueue[T]) Next() (item T) { pq.muMessages.Lock() if len(pq.items) > 0 { item = heap.Pop(pq).(T) } pq.muMessages.Unlock() return } func (pq *PriorityQueue[T]) Size() (l int) { pq.muMessages.RLock() l = pq.Len() pq.muMessages.RUnlock() return } func (pq *PriorityQueue[T]) Len() (l int) { l = len(pq.items) return } func (pq *PriorityQueue[T]) Less(i, j int) bool { // We want Pop to give us the lowest, not highest, priority so we use lower than here. return pq.items[i].Counter() < pq.items[j].Counter() } func (pq *PriorityQueue[T]) Swap(i, j int) { pq.items[i], pq.items[j] = pq.items[j], pq.items[i] } func (pq *PriorityQueue[T]) Push(x any) { pq.items = append(pq.items, x.(T)) pq.metrics.ItemQueued(pq.name, x.(T)) } func (pq *PriorityQueue[T]) Pop() (item any) { var null T if n := len(pq.items); n > 0 { item = pq.items[n-1] pq.metrics.ItemPop(pq.name, item.(T)) pq.items, pq.items[n-1] = pq.items[:n-1], null } return item } ================================================ FILE: internal/queue/simple.go ================================================ package queue import ( "container/list" "context" "sync" ) type SimpleQueue[T any] struct { name string list *list.List metrics MetricsTracer[T] signal chan struct{} mu sync.Mutex } func NewSimpleQueue[T any](name string, tracer MetricsTracer[T]) *SimpleQueue[T] { return &SimpleQueue[T]{ name: name, metrics: tracer, list: list.New(), signal: make(chan struct{}), } } // Add pushes an item to the queue func (q *SimpleQueue[T]) Add(m T) { q.mu.Lock() defer q.mu.Unlock() _ = q.list.PushBack(m) q.metrics.ItemQueued(q.name, m) // signal that we got a new item select { case q.signal <- struct{}{}: default: } } // Pop removes and returns the first item from the queue. // If the queue is empty, the second returned value will be false. func (q *SimpleQueue[T]) Pop() (m T, ok bool) { q.mu.Lock() defer q.mu.Unlock() if q.list.Len() > 0 { element := q.list.Front() q.list.Remove(element) m = element.Value.(T) ok = true } return } // WaitForItem blocks until a new item is available or the context is canceled. // It returns the new item along with a boolean value indicating whether context has expired. func (q *SimpleQueue[T]) WaitForItem(ctx context.Context) (item T, ok bool) { q.mu.Lock() defer q.mu.Unlock() // Keep attempting to retrieve a new item until the context is canceled for ctx.Err() == nil { if q.list.Len() == 0 { // queue is empty, wait for either a signal of a new item or a // context cancellation q.mu.Unlock() select { case <-q.signal: case <-ctx.Done(): } q.mu.Lock() continue } // pop front item from the queue element := q.list.Front() q.list.Remove(element) return element.Value.(T), true } return } ================================================ FILE: internal/queue/simple_test.go ================================================ package queue import ( "context" "fmt" "sync" "testing" "time" "github.com/stretchr/testify/require" ) type testSimpleQueue = *SimpleQueue[int] func newTestSimpleQueue() testSimpleQueue { return NewSimpleQueue[int]("test", &noopTracer[int]{}) } func TestQueue(t *testing.T) { queue := newTestSimpleQueue() e, ok := queue.Pop() require.Equal(t, 0, e) require.False(t, ok) queue.Add(1) e, ok = queue.Pop() require.Equal(t, 1, e) require.True(t, ok) e, ok = queue.Pop() require.Equal(t, 0, e) require.False(t, ok) } func TestSyncQueue(t *testing.T) { cases := []struct{ N int }{ {1}, {10}, {100}, {1000}, {10000}, } for _, tc := range cases { name := fmt.Sprintf("%d_elements", tc.N) t.Run(name, func(t *testing.T) { queue := newTestSimpleQueue() for i := 0; i < tc.N; i++ { queue.Add(i + 1) } for i := 0; i < tc.N; i++ { e, ok := queue.Pop() require.Equal(t, i+1, e) require.True(t, ok) } }) } } func TestAsyncQueue(t *testing.T) { cases := []struct{ N int }{ {1}, {10}, {100}, {1000}, {10000}, } for _, tc := range cases { name := fmt.Sprintf("%d_elements", tc.N) t.Run(name, func(t *testing.T) { queue := newTestSimpleQueue() wg := sync.WaitGroup{} wg.Add(tc.N) elems := map[int]struct{}{} for i := 0; i < tc.N; i++ { elems[i+1] = struct{}{} go func(i int) { queue.Add(i + 1) wg.Done() }(i) } wg.Wait() for i := 0; i < tc.N; i++ { e, ok := queue.Pop() require.True(t, ok) _, exist := elems[e] require.True(t, exist) delete(elems, e) } require.Len(t, elems, 0) }) } } func TestWaitnForItemQueue(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() cases := []struct{ N int }{ {1}, {10}, {100}, {1000}, {10000}, } for _, tc := range cases { name := fmt.Sprintf("%d_elements", tc.N) t.Run(name, func(t *testing.T) { ctx, cancel := context.WithCancel(ctx) defer cancel() queue := newTestSimpleQueue() cc := make(chan int, tc.N) go func() { defer close(cc) for { e, ok := queue.WaitForItem(ctx) if !ok { return } cc <- e } }() for i := 0; i < tc.N; i++ { queue.Add(i + 1) } for i := 0; i < tc.N; i++ { select { case e := <-cc: require.Equal(t, i+1, e) case <-time.After(time.Second): require.FailNow(t, "timeout while waiting for event") } } }) } } ================================================ FILE: internal/sysutil/sysutil.go ================================================ package sysutil import ( "os" "runtime" "syscall" "go.uber.org/multierr" "moul.io/openfiles" "berty.tech/weshnet/v2/internal/bertyversion" "berty.tech/weshnet/v2/pkg/protocoltypes" "berty.tech/weshnet/v2/pkg/username" ) func SystemInfoProcess() (*protocoltypes.SystemInfo_Process, error) { var errs error // openfiles nofile, nofileErr := openfiles.Count() errs = multierr.Append(errs, nofileErr) // hostname hn, err := os.Hostname() errs = multierr.Append(errs, err) // working dir wd, err := syscall.Getwd() errs = multierr.Append(errs, err) reply := protocoltypes.SystemInfo_Process{ Nofile: nofile, TooManyOpenFiles: openfiles.IsTooManyError(nofileErr), NumCpu: int64(runtime.NumCPU()), GoVersion: runtime.Version(), HostName: hn, NumGoroutine: int64(runtime.NumGoroutine()), OperatingSystem: runtime.GOOS, Arch: runtime.GOARCH, Version: bertyversion.Version, VcsRef: bertyversion.VcsRef, Pid: int64(syscall.Getpid()), Uid: int64(syscall.Getuid()), Ppid: int64(syscall.Getppid()), WorkingDir: wd, SystemUsername: username.GetUsername(), } // see sysutil_.go files err = appendCustomSystemInfo(&reply) errs = multierr.Append(errs, err) return &reply, errs } ================================================ FILE: internal/sysutil/sysutil_unix.go ================================================ //go:build linux || darwin package sysutil import ( "syscall" "go.uber.org/multierr" "berty.tech/weshnet/v2/pkg/protocoltypes" ) func appendCustomSystemInfo(reply *protocoltypes.SystemInfo_Process) error { var errs error // rlimit rlimitNofile := syscall.Rlimit{} err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rlimitNofile) errs = multierr.Append(errs, err) reply.RlimitCur = rlimitNofile.Cur reply.RlimitMax = rlimitNofile.Max // rusage rusage := syscall.Rusage{} err = syscall.Getrusage(syscall.RUSAGE_SELF, &rusage) errs = multierr.Append(errs, err) reply.UserCpuTimeMs = int64(rusage.Utime.Sec*1000) + int64(rusage.Utime.Usec/1000) // nolint:unconvert // on some archs, those vars may be int32 instead of int64 reply.SystemCpuTimeMs = int64(rusage.Stime.Sec*1000) + int64(rusage.Stime.Usec/1000) // nolint:unconvert // on some archs, those vars may be int32 instead of int64 // process priority prio, err := syscall.Getpriority(syscall.PRIO_PROCESS, 0) errs = multierr.Append(errs, err) reply.Priority = int64(prio) return errs } ================================================ FILE: internal/sysutil/sysutil_unsupported.go ================================================ //go:build !linux && !darwin package sysutil import "berty.tech/weshnet/v2/pkg/protocoltypes" func appendCustomSystemInfo(reply *protocoltypes.SystemInfo_Process) error { return nil } ================================================ FILE: internal/tools/example_test.go ================================================ package tools_test ================================================ FILE: internal/tools/tools.go ================================================ //go:build tools // Package tools ensures that `go mod` detect some required dependencies. // // This package should not be imported directly. package tools import ( // build tool _ "github.com/buicongtan1997/protoc-gen-swagger-config" // required by Makefile _ "github.com/daixiang0/gci" // required by protoc _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway" // required by protoc _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger" // required by Makefile _ "github.com/mdomke/git-semver/v5" // required by protoc _ "github.com/pseudomuto/protoc-gen-doc/cmd/protoc-gen-doc" // required by protoc _ "github.com/srikrsna/protoc-gen-gotag" // required by protoc _ "github.com/srikrsna/protoc-gen-gotag/tagger" // required by protoc _ "golang.org/x/tools/cmd/goimports" // required by protoc _ "google.golang.org/grpc/cmd/protoc-gen-go-grpc" // required by protoc _ "google.golang.org/protobuf/cmd/protoc-gen-go" // required by protoc _ "google.golang.org/protobuf/proto" // required by Makefile _ "moul.io/testman" // required by Makefile _ "mvdan.cc/gofumpt" ) ================================================ FILE: internal/tools/tools_untool.go ================================================ //go:build !tools // Package tools ensures that `go mod` detect some required dependencies. // // This package should not be imported directly. // // This file is a noop to make `go list` happy. package tools ================================================ FILE: message_marshaler.go ================================================ package weshnet import ( "encoding/json" "fmt" "sync" "github.com/libp2p/go-libp2p/core/crypto" peer "github.com/libp2p/go-libp2p/core/peer" "google.golang.org/protobuf/proto" "berty.tech/go-ipfs-log/enc" "berty.tech/go-ipfs-log/entry" "berty.tech/go-orbit-db/iface" "berty.tech/weshnet/v2/pkg/protocoltypes" "berty.tech/weshnet/v2/pkg/rendezvous" "berty.tech/weshnet/v2/pkg/secretstore" ) type PeerDeviceGroup struct { Group *protocoltypes.Group DevicePK crypto.PubKey } type OrbitDBMessageMarshaler struct { rp *rendezvous.RotationInterval sharedKeys map[string]enc.SharedKey topicGroup map[string]*protocoltypes.Group deviceCaches map[peer.ID]*PeerDeviceGroup muMarshall sync.RWMutex selfid peer.ID secretStore secretstore.SecretStore // in Replication Mode DeviceKey should not be sent useReplicationMode bool } func NewOrbitDBMessageMarshaler(selfid peer.ID, secretStore secretstore.SecretStore, rp *rendezvous.RotationInterval, useReplicationMode bool) *OrbitDBMessageMarshaler { return &OrbitDBMessageMarshaler{ selfid: selfid, sharedKeys: make(map[string]enc.SharedKey), deviceCaches: make(map[peer.ID]*PeerDeviceGroup), topicGroup: make(map[string]*protocoltypes.Group), rp: rp, secretStore: secretStore, useReplicationMode: useReplicationMode, } } func (m *OrbitDBMessageMarshaler) RegisterSharedKeyForTopic(topic string, sk enc.SharedKey) { m.muMarshall.Lock() m.sharedKeys[topic] = sk m.muMarshall.Unlock() } func (m *OrbitDBMessageMarshaler) RegisterGroup(sid string, group *protocoltypes.Group) { m.muMarshall.Lock() m.topicGroup[sid] = group m.muMarshall.Unlock() } func (m *OrbitDBMessageMarshaler) GetDevicePKForPeerID(id peer.ID) (pdg *PeerDeviceGroup, ok bool) { m.muMarshall.RLock() pdg, ok = m.deviceCaches[id] m.muMarshall.RUnlock() return } func (m *OrbitDBMessageMarshaler) getSharedKeyFor(topic string) (sk enc.SharedKey, ok bool) { sk, ok = m.sharedKeys[topic] return } func (m *OrbitDBMessageMarshaler) Marshal(msg *iface.MessageExchangeHeads) ([]byte, error) { topic := msg.Address m.muMarshall.RLock() defer m.muMarshall.RUnlock() // marshall binary always return nil has error pid, _ := m.selfid.MarshalBinary() point, err := m.rp.PointForTopic(topic) if err != nil { return nil, fmt.Errorf("unable to get rendezvous for period: %w", err) } group, ok := m.topicGroup[topic] if !ok { return nil, fmt.Errorf("unknown group for topic: %s", topic) } var ownPK []byte // in replication mode, it doesn't make sense to send DevicePK if !m.useReplicationMode { ownDevice, err := m.secretStore.GetOwnMemberDeviceForGroup(group) if err != nil { return nil, fmt.Errorf("unable to get own member device key for group: %w", err) } ownPK, err = ownDevice.Device().Raw() if err != nil { return nil, fmt.Errorf("unable to get raw pk for device: %w", err) } } // @TODO(gfanton): use protobuf for this ? heads, err := json.Marshal(msg.Heads) if err != nil { return nil, fmt.Errorf("unable to marshal heads: %w", err) } box := &protocoltypes.OrbitDBMessageHeads_Box{ Address: msg.Address, Heads: heads, DevicePk: ownPK, PeerId: pid, } sealedBox, err := m.sealBox(msg.Address, box) if err != nil { return nil, fmt.Errorf("unable to seal box: %w", err) } msghead := protocoltypes.OrbitDBMessageHeads{ RawRotation: point.RawRotationTopic(), SealedBox: sealedBox, } payload, err := proto.Marshal(&msghead) if err != nil { return nil, fmt.Errorf("unable to marshal payload: %w", err) } return payload, nil } func (m *OrbitDBMessageMarshaler) Unmarshal(payload []byte, msg *iface.MessageExchangeHeads) error { m.muMarshall.Lock() defer m.muMarshall.Unlock() if msg == nil { msg = &iface.MessageExchangeHeads{} } msghead := protocoltypes.OrbitDBMessageHeads{} if err := proto.Unmarshal(payload, &msghead); err != nil { return fmt.Errorf("unable to unmarshal payload `%x`: %w", payload, err) } rotation := msghead.GetRawRotation() point, err := m.rp.PointForRawRotation(rotation) if err != nil { return fmt.Errorf("unable to get topic for rendezvous: %w", err) } box, err := m.openBox(point.Topic(), msghead.GetSealedBox()) if err != nil { return fmt.Errorf("unable to open sealed box: %w", err) } var entries []*entry.Entry if err := json.Unmarshal(box.Heads, &entries); err != nil { return fmt.Errorf("unable to unmarshal entries: %w", err) } msg.Address = box.Address msg.Heads = entries if box.DevicePk == nil { // @NOTE(gfanton): this is probably a message from a replication server // which should not have a DevicePK return nil } pid, err := peer.IDFromBytes(box.PeerId) if err != nil { return fmt.Errorf("unable to parse peer id: %w", err) } // store device into cache var pdg PeerDeviceGroup pub, err := crypto.UnmarshalEd25519PublicKey(box.DevicePk) if err != nil { return fmt.Errorf("unable to unmarshal remote device pk: %w", err) } pdg.DevicePK = pub group, ok := m.topicGroup[msg.Address] if ok { // @FIXME(gfanton): do we need to raise an error here ? pdg.Group = group } m.deviceCaches[pid] = &pdg return nil } func (m *OrbitDBMessageMarshaler) sealBox(topic string, box *protocoltypes.OrbitDBMessageHeads_Box) ([]byte, error) { sk, ok := m.getSharedKeyFor(topic) if !ok { return nil, fmt.Errorf("unable to get shared key for topic") } rawBox, err := proto.Marshal(box) if err != nil { return nil, fmt.Errorf("unable to marshal box %w", err) } sealedBox, err := sk.Seal(rawBox) if err != nil { return nil, fmt.Errorf("unable to seal box: %w", err) } return sealedBox, nil } func (m *OrbitDBMessageMarshaler) openBox(topic string, payload []byte) (*protocoltypes.OrbitDBMessageHeads_Box, error) { sk, ok := m.getSharedKeyFor(topic) if !ok { return nil, fmt.Errorf("unable to get shared key for topic") } rawBox, err := sk.Open(payload) if err != nil { return nil, fmt.Errorf("unable to open sealed box: %w", err) } box := &protocoltypes.OrbitDBMessageHeads_Box{} if err := proto.Unmarshal(rawBox, box); err != nil { return nil, fmt.Errorf("unable to unmarshal box: %w", err) } return box, nil } ================================================ FILE: message_marshaler_test.go ================================================ package weshnet import ( "testing" "time" mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "berty.tech/go-ipfs-log/enc" "berty.tech/go-ipfs-log/entry" "berty.tech/go-orbit-db/iface" "berty.tech/weshnet/v2/pkg/rendezvous" "berty.tech/weshnet/v2/pkg/secretstore" ) var ( testSeed1 = []byte("secretsecretsecretsecretsecretse") // 32 bytes seed testSeed2 = []byte("badbadbadbadbadbadbadbadbadbadba") // 32 bytes seed ) func TestRotationMessageMarshaler(t *testing.T) { key, err := enc.NewSecretbox(testSeed1) require.NoError(t, err) msg := &iface.MessageExchangeHeads{ Address: "address_1", Heads: []*entry.Entry{}, } mn := mocknet.New() defer mn.Close() p, err := mn.GenPeer() require.NoError(t, err) // generate keystore acc1, err := secretstore.NewInMemSecretStore(nil) require.NoError(t, err) rp := rendezvous.NewStaticRotationInterval() m := NewOrbitDBMessageMarshaler(p.ID(), acc1, rp, false) g, _, err := NewGroupMultiMember() require.NoError(t, err) m.RegisterGroup(msg.Address, g) rp.RegisterRotation(time.Now(), msg.Address, testSeed1) m.RegisterSharedKeyForTopic(msg.Address, key) // marshal with register topic, should succeed payload, err := m.Marshal(msg) require.NoError(t, err) require.NotNil(t, payload) ret := iface.MessageExchangeHeads{} err = m.Unmarshal(payload, &ret) require.NoError(t, err) assert.Equal(t, msg.Address, ret.Address) } func TestRotationMessageMarshalUnknownTopic(t *testing.T) { mn := mocknet.New() defer mn.Close() msg := &iface.MessageExchangeHeads{ Address: "address_1", Heads: []*entry.Entry{}, } p, err := mn.GenPeer() require.NoError(t, err) // generate keystore acc, err := secretstore.NewInMemSecretStore(nil) require.NoError(t, err) rp := rendezvous.NewStaticRotationInterval() m := NewOrbitDBMessageMarshaler(p.ID(), acc, rp, false) // marshal without register topic, should fail payload, err := m.Marshal(msg) require.Error(t, err) require.Nil(t, payload) } func TestRotationMessageUnmarshalUnknownTopic(t *testing.T) { mn := mocknet.New() defer mn.Close() msg := &iface.MessageExchangeHeads{ Address: "address_1", Heads: []*entry.Entry{}, } key1, err := enc.NewSecretbox(testSeed1) require.NoError(t, err) key2, err := enc.NewSecretbox(testSeed2) require.NoError(t, err) p1, err := mn.GenPeer() require.NoError(t, err) p2, err := mn.GenPeer() require.NoError(t, err) // generate keystore acc1, err := secretstore.NewInMemSecretStore(nil) require.NoError(t, err) acc2, err := secretstore.NewInMemSecretStore(nil) require.NoError(t, err) g1, _, err := NewGroupMultiMember() require.NoError(t, err) g2, _, err := NewGroupMultiMember() require.NoError(t, err) rp1 := rendezvous.NewStaticRotationInterval() rp1.RegisterRotation(time.Now(), msg.Address, testSeed1) rp2 := rendezvous.NewStaticRotationInterval() m1 := NewOrbitDBMessageMarshaler(p1.ID(), acc1, rp1, false) m1.RegisterSharedKeyForTopic(msg.Address, key1) m1.RegisterGroup(msg.Address, g1) payload, err := m1.Marshal(msg) require.NoError(t, err) m2 := NewOrbitDBMessageMarshaler(p2.ID(), acc2, rp2, false) m2.RegisterSharedKeyForTopic(msg.Address, key2) m2.RegisterGroup(msg.Address, g2) var ret iface.MessageExchangeHeads // marshal with wrong key should fail err = m2.Unmarshal(payload, &ret) require.Error(t, err) assert.NotEqual(t, ret.Address, msg.Address) } func TestRotationMessageMarshalWrongKey(t *testing.T) { mn := mocknet.New() defer mn.Close() msg := &iface.MessageExchangeHeads{ Address: "address_1", Heads: []*entry.Entry{}, } key1, err := enc.NewSecretbox(testSeed1) require.NoError(t, err) key2, err := enc.NewSecretbox(testSeed2) require.NoError(t, err) p1, err := mn.GenPeer() require.NoError(t, err) p2, err := mn.GenPeer() require.NoError(t, err) g1, _, err := NewGroupMultiMember() require.NoError(t, err) g2, _, err := NewGroupMultiMember() require.NoError(t, err) // generate keystore acc1, err := secretstore.NewInMemSecretStore(nil) require.NoError(t, err) acc2, err := secretstore.NewInMemSecretStore(nil) require.NoError(t, err) rp1 := rendezvous.NewStaticRotationInterval() rp1.RegisterRotation(time.Now(), msg.Address, testSeed1) rp2 := rendezvous.NewStaticRotationInterval() rp2.RegisterRotation(time.Now(), msg.Address, testSeed2) m1 := NewOrbitDBMessageMarshaler(p1.ID(), acc1, rp1, false) m1.RegisterSharedKeyForTopic(msg.Address, key1) m1.RegisterGroup(msg.Address, g1) payload, err := m1.Marshal(msg) require.NoError(t, err) m2 := NewOrbitDBMessageMarshaler(p2.ID(), acc2, rp2, false) m2.RegisterSharedKeyForTopic(msg.Address, key2) m2.RegisterGroup(msg.Address, g2) var ret iface.MessageExchangeHeads // marshal with wrong key should fail err = m2.Unmarshal(payload, &ret) require.Error(t, err) assert.NotEqual(t, ret.Address, msg.Address) // marshal with good key should succeed err = m1.Unmarshal(payload, &ret) require.NoError(t, err) assert.Equal(t, ret.Address, msg.Address) } ================================================ FILE: orbitdb.go ================================================ package weshnet import ( "context" "encoding/base64" "fmt" "sync" "time" "github.com/ipfs/go-cid" "github.com/ipfs/go-datastore" ds_sync "github.com/ipfs/go-datastore/sync" coreiface "github.com/ipfs/kubo/core/coreiface" "github.com/libp2p/go-libp2p/core/crypto" peer "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/p2p/host/eventbus" "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" "go.uber.org/zap" ipfslog "berty.tech/go-ipfs-log" "berty.tech/go-ipfs-log/enc" "berty.tech/go-ipfs-log/entry" "berty.tech/go-ipfs-log/identityprovider" "berty.tech/go-ipfs-log/io" orbitdb "berty.tech/go-orbit-db" "berty.tech/go-orbit-db/baseorbitdb" "berty.tech/go-orbit-db/iface" "berty.tech/go-orbit-db/pubsub/pubsubcoreapi" "berty.tech/go-orbit-db/stores" "berty.tech/weshnet/v2/pkg/errcode" "berty.tech/weshnet/v2/pkg/ipfsutil" "berty.tech/weshnet/v2/pkg/protocoltypes" "berty.tech/weshnet/v2/pkg/rendezvous" "berty.tech/weshnet/v2/pkg/secretstore" "berty.tech/weshnet/v2/pkg/tyber" ) type GroupOpenMode uint64 const ( GroupOpenModeUndefined GroupOpenMode = iota GroupOpenModeReplicate GroupOpenModeWrite ) var _ = GroupOpenModeUndefined type loggable interface { setLogger(*zap.Logger) } type NewOrbitDBOptions struct { baseorbitdb.NewOrbitDBOptions Datastore datastore.Batching SecretStore secretstore.SecretStore RotationInterval *rendezvous.RotationInterval PrometheusRegister prometheus.Registerer GroupMetadataStoreType string GroupMessageStoreType string ReplicationMode bool } func (n *NewOrbitDBOptions) applyDefaults() { if n.Datastore == nil { n.Datastore = ds_sync.MutexWrap(datastore.NewMapDatastore()) } if n.PrometheusRegister == nil { n.PrometheusRegister = prometheus.DefaultRegisterer } if n.Cache == nil { n.Cache = NewOrbitDatastoreCache(n.Datastore) } if n.Logger == nil { n.Logger = zap.NewNop() } if n.RotationInterval == nil { n.RotationInterval = rendezvous.NewStaticRotationInterval() } if n.Logger == nil { n.Logger = zap.NewNop() } n.Logger = n.Logger.Named("odb") if n.GroupMetadataStoreType == "" { n.GroupMetadataStoreType = "wesh_group_metadata" } if n.GroupMessageStoreType == "" { n.GroupMessageStoreType = "wesh_group_messages" } } type ( GroupMap = sync.Map GroupContextMap = sync.Map GroupsSigPubKeyMap = sync.Map ) type WeshOrbitDB struct { baseorbitdb.BaseOrbitDB keyStore *BertySignedKeyStore secretStore secretstore.SecretStore pubSub iface.PubSubInterface rotationInterval *rendezvous.RotationInterval messageMarshaler *OrbitDBMessageMarshaler replicationMode bool prometheusRegister prometheus.Registerer groupMetadataStoreType string groupMessageStoreType string ctx context.Context // FIXME(gfanton): use real map instead of sync.Map groups *GroupMap // map[string]*protocoltypes.Group groupContexts *GroupContextMap // map[string]*GroupContext groupsSigPubKey *GroupsSigPubKeyMap // map[string]crypto.PubKey } func (s *WeshOrbitDB) registerGroupPrivateKey(g *protocoltypes.Group) error { groupID := g.GroupIDAsString() gSigSK, err := g.GetSigningPrivKey() if err != nil { return errcode.ErrCode_TODO.Wrap(err) } if err := s.SetGroupSigPubKey(groupID, gSigSK.GetPublic()); err != nil { return errcode.ErrCode_TODO.Wrap(err) } if err := s.keyStore.SetKey(gSigSK); err != nil { return errcode.ErrCode_TODO.Wrap(err) } return nil } func (s *WeshOrbitDB) registerGroupSigningPubKey(g *protocoltypes.Group) error { groupID := g.GroupIDAsString() var gSigPK crypto.PubKey gSigSK, err := g.GetSigningPrivKey() if err == nil && gSigSK != nil { gSigPK = gSigSK.GetPublic() } else { gSigPK, err = g.GetSigningPubKey() if err != nil { return errcode.ErrCode_TODO.Wrap(err) } } if err := s.SetGroupSigPubKey(groupID, gSigPK); err != nil { return errcode.ErrCode_TODO.Wrap(err) } return nil } func NewWeshOrbitDB(ctx context.Context, ipfs coreiface.CoreAPI, options *NewOrbitDBOptions) (*WeshOrbitDB, error) { var err error if options == nil { options = &NewOrbitDBOptions{} } options.applyDefaults() ks := &BertySignedKeyStore{} options.Keystore = ks options.Identity = &identityprovider.Identity{} self, err := ipfs.Key().Self(ctx) if err != nil { return nil, err } if options.PubSub == nil { options.PubSub = pubsubcoreapi.NewPubSub(ipfs, self.ID(), time.Second, options.Logger, options.Tracer) } mm := NewOrbitDBMessageMarshaler(self.ID(), options.SecretStore, options.RotationInterval, options.ReplicationMode) options.MessageMarshaler = mm orbitDB, err := baseorbitdb.NewOrbitDB(ctx, ipfs, &options.NewOrbitDBOptions) if err != nil { return nil, errcode.ErrCode_TODO.Wrap(err) } bertyDB := &WeshOrbitDB{ ctx: ctx, messageMarshaler: mm, BaseOrbitDB: orbitDB, keyStore: ks, secretStore: options.SecretStore, rotationInterval: options.RotationInterval, pubSub: options.PubSub, groups: &GroupMap{}, groupContexts: &GroupContextMap{}, // map[string]*GroupContext groupsSigPubKey: &GroupsSigPubKeyMap{}, // map[string]crypto.PubKey groupMetadataStoreType: options.GroupMetadataStoreType, groupMessageStoreType: options.GroupMessageStoreType, replicationMode: options.ReplicationMode, prometheusRegister: options.PrometheusRegister, } if err := bertyDB.RegisterAccessControllerType(NewSimpleAccessController); err != nil { return nil, errcode.ErrCode_TODO.Wrap(err) } bertyDB.RegisterStoreType(bertyDB.groupMetadataStoreType, constructorFactoryGroupMetadata(bertyDB, options.Logger)) bertyDB.RegisterStoreType(bertyDB.groupMessageStoreType, constructorFactoryGroupMessage(bertyDB, options.Logger)) return bertyDB, nil } func (s *WeshOrbitDB) openAccountGroup(ctx context.Context, options *orbitdb.CreateDBOptions, ipfsCoreAPI ipfsutil.ExtendedCoreAPI) (*GroupContext, error) { l := s.Logger() if options == nil { options = &orbitdb.CreateDBOptions{} } if options.EventBus == nil { options.EventBus = s.EventBus() } group, _, err := s.secretStore.GetGroupForAccount() if err != nil { return nil, errcode.ErrCode_ErrOrbitDBOpen.Wrap(err) } l.Debug("Got account group", tyber.FormatStepLogFields(ctx, []tyber.Detail{{Name: "Group", Description: group.String()}})...) gc, err := s.OpenGroup(ctx, group, options) if err != nil { return nil, errcode.ErrCode_ErrGroupOpen.Wrap(err) } l.Debug("Opened account group", tyber.FormatStepLogFields(ctx, []tyber.Detail{})...) gc.TagGroupContextPeers(ipfsCoreAPI, 84) if err := gc.ActivateGroupContext(nil); err != nil { return nil, errcode.ErrCode_TODO.Wrap(err) } l.Debug("TagGroupContextPeers done", tyber.FormatStepLogFields(ctx, []tyber.Detail{})...) return gc, nil } func (s *WeshOrbitDB) setHeadsForGroup(ctx context.Context, g *protocoltypes.Group, metaHeads, messageHeads []cid.Cid) error { groupID := g.GroupIDAsString() var ( err error metaImpl, messagesImpl orbitdb.Store ) existingGC, err := s.getGroupContext(groupID) if err != nil && !errcode.Is(err, errcode.ErrCode_ErrMissingMapKey) { return errcode.ErrCode_ErrInternal.Wrap(err) } if err == nil { metaImpl = existingGC.metadataStore messagesImpl = existingGC.messageStore } if metaImpl == nil || messagesImpl == nil { s.groups.Store(groupID, g) if err := s.registerGroupSigningPubKey(g); err != nil { return errcode.ErrCode_ErrInternal.Wrap(err) } s.Logger().Debug("OpenGroup", zap.Any("public key", g.PublicKey), zap.Any("secret", g.Secret), zap.Stringer("type", g.GroupType)) if metaImpl == nil { metaImpl, err = s.storeForGroup(ctx, s, g, nil, s.groupMetadataStoreType, GroupOpenModeReplicate) if err != nil { return errcode.ErrCode_ErrOrbitDBOpen.Wrap(err) } defer func() { _ = metaImpl.Close() }() } if messagesImpl == nil { messagesImpl, err = s.storeForGroup(ctx, s, g, nil, s.groupMessageStoreType, GroupOpenModeReplicate) if err != nil { return errcode.ErrCode_ErrOrbitDBOpen.Wrap(err) } defer func() { _ = messagesImpl.Close() }() } } if messagesImpl == nil { return errcode.ErrCode_ErrInternal.Wrap(fmt.Errorf("message store is nil")) } if metaImpl == nil { return errcode.ErrCode_ErrInternal.Wrap(fmt.Errorf("metadata store is nil")) } var wg sync.WaitGroup // load and wait heads for metadata and message stores wg.Add(2) go func() { // load meta heads if err := s.loadHeads(ctx, metaImpl, metaHeads); err != nil { s.Logger().Error("unable to load metadata heads", zap.Error(err)) } wg.Done() }() go func() { // load message heads if err := s.loadHeads(ctx, messagesImpl, messageHeads); err != nil { s.Logger().Error("unable to load message heads", zap.Error(err)) } wg.Done() }() wg.Wait() return nil } func (s *WeshOrbitDB) loadHeads(ctx context.Context, store iface.Store, heads []cid.Cid) (err error) { sub, err := store.EventBus().Subscribe(new(stores.EventReplicated), eventbus.Name("weshnet/load-heads")) if err != nil { return fmt.Errorf("unable to subscribe to EventReplicated") } defer sub.Close() // check and generate missing entries if needed headsEntries := make([]ipfslog.Entry, len(heads)) for i, h := range heads { if _, ok := store.OpLog().Get(h); !ok { headsEntries[i] = &entry.Entry{Hash: h} } } if len(headsEntries) == 0 { return nil } store.Replicator().Load(ctx, headsEntries) for found := 0; found < len(heads); { // wait for load to finish select { case e := <-sub.Out(): evt := e.(stores.EventReplicated) // iterate over entries from replicated event to search for our heads for _, headEntry := range headsEntries { for _, evtEntry := range evt.Entries { if evtEntry.Equals(headEntry) { found++ break } } } case <-s.ctx.Done(): return s.ctx.Err() } } return nil } func (s *WeshOrbitDB) OpenGroup(ctx context.Context, g *protocoltypes.Group, options *orbitdb.CreateDBOptions) (*GroupContext, error) { if s.secretStore == nil { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("db open in naive mode")) } groupID := g.GroupIDAsString() existingGC, err := s.getGroupContext(groupID) if err != nil && !errcode.Is(err, errcode.ErrCode_ErrMissingMapKey) { return nil, errcode.ErrCode_ErrInternal.Wrap(err) } if err == nil { return existingGC, nil } s.groups.Store(groupID, g) if err := s.registerGroupPrivateKey(g); err != nil { return nil, err } s.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)))...) memberDevice, err := s.secretStore.GetOwnMemberDeviceForGroup(g) if err != nil { return nil, errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err) } mpkb, err := crypto.MarshalPublicKey(memberDevice.Member()) if err != nil { mpkb = []byte{} } s.Logger().Debug("Got member device", tyber.FormatStepLogFields(s.ctx, []tyber.Detail{{Name: "DevicePublicKey", Description: base64.RawURLEncoding.EncodeToString(mpkb)}})...) // Force secret generation if missing if _, err := s.secretStore.GetShareableChainKey(s.ctx, g, memberDevice.Member()); err != nil { return nil, errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err) } s.Logger().Debug("Got device chain key", tyber.FormatStepLogFields(s.ctx, []tyber.Detail{})...) metaImpl, err := s.groupMetadataStore(ctx, g, options) if err != nil { return nil, errcode.ErrCode_ErrOrbitDBOpen.Wrap(err) } s.messageMarshaler.RegisterGroup(metaImpl.Address().String(), g) s.Logger().Debug("Got metadata store", tyber.FormatStepLogFields(s.ctx, []tyber.Detail{})...) // force to unshare the same EventBus between groupMetadataStore and groupMessageStore // to avoid having a bunch of events which are not for the correct group if options != nil && options.EventBus != nil { options.EventBus = eventbus.NewBus( eventbus.WithMetricsTracer(eventbus.NewMetricsTracer(eventbus.WithRegisterer(s.prometheusRegister)))) } messagesImpl, err := s.groupMessageStore(ctx, g, options) if err != nil { metaImpl.Close() return nil, errcode.ErrCode_ErrOrbitDBOpen.Wrap(err) } s.messageMarshaler.RegisterGroup(messagesImpl.Address().String(), g) s.Logger().Debug("Got message store", tyber.FormatStepLogFields(s.ctx, []tyber.Detail{})...) gc := NewContextGroup(g, metaImpl, messagesImpl, s.secretStore, memberDevice, s.Logger()) s.Logger().Debug("Created group context", tyber.FormatStepLogFields(s.ctx, []tyber.Detail{})...) s.groupContexts.Store(groupID, gc) s.Logger().Debug("Stored group context", tyber.FormatStepLogFields(s.ctx, []tyber.Detail{})...) return gc, nil } func (s *WeshOrbitDB) OpenGroupReplication(ctx context.Context, g *protocoltypes.Group, options *orbitdb.CreateDBOptions) (iface.Store, iface.Store, error) { if g == nil || len(g.PublicKey) == 0 { return nil, nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("missing group or group pubkey")) } groupID := g.GroupIDAsString() gc, err := s.getGroupContext(groupID) if err != nil && !errcode.Is(err, errcode.ErrCode_ErrMissingMapKey) { return nil, nil, errcode.ErrCode_ErrInternal.Wrap(err) } if err == nil { return gc.metadataStore, gc.messageStore, nil } s.groups.Store(groupID, g) if err := s.registerGroupSigningPubKey(g); err != nil { return nil, nil, err } metadataStore, err := s.storeForGroup(ctx, s, g, options, s.groupMetadataStoreType, GroupOpenModeReplicate) if err != nil { _ = metadataStore.Close() return nil, nil, errors.Wrap(err, "unable to open database") } messageStore, err := s.storeForGroup(ctx, s, g, options, s.groupMessageStoreType, GroupOpenModeReplicate) if err != nil { return nil, nil, errors.Wrap(err, "unable to open database") } return metadataStore, messageStore, nil } func (s *WeshOrbitDB) getGroupContext(id string) (*GroupContext, error) { g, ok := s.groupContexts.Load(id) if !ok { return nil, errcode.ErrCode_ErrMissingMapKey } gc, ok := g.(*GroupContext) if !ok { s.groupContexts.Delete(id) return nil, errors.New("cannot cast object to GroupContext") } if gc.IsClosed() { s.groupContexts.Delete(id) return nil, errcode.ErrCode_ErrMissingMapKey } return g.(*GroupContext), nil } // SetGroupSigPubKey registers a new group signature pubkey, mainly used to // replicate a store data without needing to access to its content func (s *WeshOrbitDB) SetGroupSigPubKey(groupID string, pubKey crypto.PubKey) error { if pubKey == nil { return errcode.ErrCode_ErrInvalidInput } s.groupsSigPubKey.Store(groupID, pubKey) return nil } func (s *WeshOrbitDB) storeForGroup(ctx context.Context, o iface.BaseOrbitDB, g *protocoltypes.Group, options *orbitdb.CreateDBOptions, storeType string, groupOpenMode GroupOpenMode) (iface.Store, error) { l := s.Logger() if options == nil { options = &orbitdb.CreateDBOptions{} } // setup eventbus metrics if options.EventBus == nil { options.EventBus = eventbus.NewBus(eventbus.WithMetricsTracer(eventbus.NewMetricsTracer(eventbus.WithRegisterer(s.prometheusRegister)))) } options, err := DefaultOrbitDBOptions(g, options, s.keyStore, storeType, groupOpenMode) if err != nil { return nil, err } l.Debug("Opening store", tyber.FormatStepLogFields(ctx, []tyber.Detail{{Name: "Group", Description: g.String()}, {Name: "Options", Description: fmt.Sprint(options)}}, tyber.Status(tyber.Running))...) options.StoreType = &storeType name := fmt.Sprintf("%s_%s", g.GroupIDAsString(), storeType) addr, err := o.DetermineAddress(ctx, name, storeType, &orbitdb.DetermineAddressOptions{AccessController: options.AccessController}) if err != nil { return nil, err } s.messageMarshaler.RegisterGroup(addr.String(), g) linkKey, err := g.GetLinkKeyArray() if err != nil { return nil, err } if key := linkKey[:]; len(key) > 0 { sk, err := enc.NewSecretbox(key) if err != nil { return nil, err } cborIO := io.CBOR() cborIO.ApplyOptions(&io.CBOROptions{LinkKey: sk}) options.IO = cborIO l.Debug("opening store: register rotation", zap.String("topic", addr.String())) s.messageMarshaler.RegisterSharedKeyForTopic(addr.String(), sk) s.rotationInterval.RegisterRotation(time.Now(), addr.String(), key) } store, err := o.Open(ctx, name, options) if err != nil { return nil, errcode.ErrCode_ErrOrbitDBOpen.Wrap(err) } l.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))...) _ = store.Load(ctx, -1) l.Debug("Loaded store", tyber.FormatStepLogFields(ctx, []tyber.Detail{{Name: "Group", Description: g.String()}})...) return store, nil } func (s *WeshOrbitDB) groupMetadataStore(ctx context.Context, g *protocoltypes.Group, options *orbitdb.CreateDBOptions) (*MetadataStore, error) { if options == nil { options = &orbitdb.CreateDBOptions{} } l := s.Logger().Named("metadataStore") options.Logger = l l.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))...) store, err := s.storeForGroup(ctx, s, g, options, s.groupMetadataStoreType, GroupOpenModeWrite) if err != nil { return nil, tyber.LogFatalError(ctx, l, "Failed to get group store", errors.Wrap(err, "unable to open database")) } l.Debug("Got group store", tyber.FormatStepLogFields(ctx, []tyber.Detail{{Name: "DBName", Description: store.DBName()}})...) sStore, ok := store.(*MetadataStore) if !ok { return nil, tyber.LogFatalError(ctx, l, "Failed to cast group store", errors.New("unable to cast store to metadata store")) } l.Debug("Opened group metadata store", tyber.FormatStepLogFields(ctx, []tyber.Detail{{Name: "Group", Description: g.String()}})...) return sStore, nil } func (s *WeshOrbitDB) groupMessageStore(ctx context.Context, g *protocoltypes.Group, options *orbitdb.CreateDBOptions) (*MessageStore, error) { if options == nil { options = &orbitdb.CreateDBOptions{} } l := s.Logger().Named("messageStore") options.Logger = l l.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))...) store, err := s.storeForGroup(ctx, s, g, options, s.groupMessageStoreType, GroupOpenModeWrite) if err != nil { return nil, errors.Wrap(err, "unable to open database") } mStore, ok := store.(*MessageStore) if !ok { return nil, errors.New("unable to cast store to message store") } return mStore, nil } func (s *WeshOrbitDB) getGroupFromOptions(options *iface.NewStoreOptions) (*protocoltypes.Group, error) { groupIDs, err := options.AccessController.GetAuthorizedByRole(identityGroupIDKey) if err != nil { return nil, errcode.ErrCode_TODO.Wrap(err) } if len(groupIDs) != 1 { return nil, errcode.ErrCode_ErrInvalidInput } g, ok := s.groups.Load(groupIDs[0]) if !ok { return nil, errcode.ErrCode_ErrInvalidInput } typed, ok := g.(*protocoltypes.Group) if !ok { return nil, errcode.ErrCode_ErrInvalidInput } return typed, nil } func (s *WeshOrbitDB) IsGroupLoaded(groupID string) bool { gc, ok := s.groups.Load(groupID) return ok && gc != nil } func (s *WeshOrbitDB) GetDevicePKForPeerID(id peer.ID) (pdg *PeerDeviceGroup, ok bool) { return s.messageMarshaler.GetDevicePKForPeerID(id) } ================================================ FILE: orbitdb_datastore_cache.go ================================================ package weshnet import ( "context" datastore "github.com/ipfs/go-datastore" "github.com/ipfs/go-datastore/query" "berty.tech/go-orbit-db/address" "berty.tech/go-orbit-db/cache" "berty.tech/weshnet/v2/internal/datastoreutil" ) type datastoreCache struct { ds datastore.Batching } //nolint:revive func (d *datastoreCache) Load(directory string, dbAddress address.Address) (datastore.Datastore, error) { return datastoreutil.NewNamespacedDatastore(d.ds, datastore.NewKey(dbAddress.String())), nil } func (d *datastoreCache) Close() error { return nil } //nolint:revive func (d *datastoreCache) Destroy(directory string, dbAddress address.Address) error { keys, err := datastoreutil.NewNamespacedDatastore(d.ds, datastore.NewKey(dbAddress.String())).Query(context.TODO(), query.Query{KeysOnly: true}) if err != nil { return nil } for { val, hasValue := keys.NextSync() if !hasValue { return nil } if err := d.ds.Delete(context.TODO(), datastore.NewKey(val.Key)); err != nil { return err } } } func NewOrbitDatastoreCache(ds datastore.Batching) cache.Interface { return &datastoreCache{ ds: datastoreutil.NewNamespacedDatastore(ds, datastore.NewKey(NamespaceOrbitDBDatastore)), } } var _ cache.Interface = (*datastoreCache)(nil) ================================================ FILE: orbitdb_many_adds_berty_test.go ================================================ package weshnet import ( "context" "fmt" "os" "path" "sync" "testing" "time" sync_ds "github.com/ipfs/go-datastore/sync" "github.com/juju/fslock" "github.com/stretchr/testify/require" "go.uber.org/zap" "berty.tech/go-orbit-db/iface" "berty.tech/weshnet/v2/pkg/ipfsutil" "berty.tech/weshnet/v2/pkg/protocoltypes" "berty.tech/weshnet/v2/pkg/secretstore" "berty.tech/weshnet/v2/pkg/testutil" ) func testAddBerty(ctx context.Context, t *testing.T, node ipfsutil.CoreAPIMock, g *protocoltypes.Group, pathBase string, storageKey []byte, storageSalt []byte, amountToAdd, amountCurrentlyPresent int) { t.Helper() testutil.FilterSpeed(t, testutil.Fast) t.Logf("TestAddBerty: amountToAdd: %d, amountCurrentlyPresent: %d\n", amountToAdd, amountCurrentlyPresent) api := node.API() ctx, cancel := context.WithCancel(ctx) defer cancel() lock := fslock.New(path.Join(pathBase, "lock")) err := lock.TryLock() require.NoError(t, err) defer lock.Unlock() baseDS, err := GetRootDatastoreForPath(pathBase, storageKey, storageSalt, zap.NewNop()) require.NoError(t, err) baseDS = sync_ds.MutexWrap(baseDS) defer testutil.Close(t, baseDS) secretStore, err := secretstore.NewSecretStore(baseDS, nil) require.NoError(t, err) defer secretStore.Close() odb, err := NewWeshOrbitDB(ctx, api, &NewOrbitDBOptions{ Datastore: baseDS, SecretStore: secretStore, }) require.NoError(t, err) defer testutil.Close(t, odb) replicate := false gc, err := odb.OpenGroup(ctx, g, &iface.CreateDBOptions{ Replicate: &replicate, }) require.NoError(t, err) defer gc.Close() defer testutil.Close(t, gc) wg := sync.WaitGroup{} wg.Add(amountToAdd * 2) amountCurrentlyFound := 0 messages, err := gc.MessageStore().ListEvents(ctx, nil, nil, false) require.NoError(t, err) for range messages { amountCurrentlyFound++ } sub, err := gc.MessageStore().EventBus().Subscribe(new(*protocoltypes.GroupMessageEvent)) require.NoError(t, err) defer sub.Close() // Watch for incoming new messages go func() { for range sub.Out() { wg.Done() } }() _, err = gc.MetadataStore().AddDeviceToGroup(ctx) require.NoError(t, err) for i := 0; i < amountToAdd; i++ { _, err := gc.MessageStore().AddMessage(ctx, []byte(fmt.Sprintf("%d", i))) require.NoError(t, err) wg.Done() } done := make(chan struct{}) go func() { wg.Wait() close(done) }() select { case <-done: case <-time.After(30 * time.Second): } require.Equal(t, amountCurrentlyPresent, amountCurrentlyFound) } func TestAddBerty(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api := ipfsutil.TestingCoreAPI(ctx, t) pathBase, err := os.MkdirTemp("", "manyaddstest") if err != nil { t.Fatal(err) } defer os.RemoveAll(pathBase) g, _, err := NewGroupMultiMember() require.NoError(t, err) storageKey := []byte("42424242424242424242424242424242") storageSalt := []byte("2121212121212121") testAddBerty(ctx, t, api, g, pathBase, storageKey, storageSalt, 20, 0) testAddBerty(ctx, t, api, g, pathBase, storageKey, storageSalt, 0, 20) testAddBerty(ctx, t, api, g, pathBase, storageKey, storageSalt, 20, 20) testAddBerty(ctx, t, api, g, pathBase, storageKey, storageSalt, 0, 40) // FIXME: use github.com/stretchr/testify/suite } ================================================ FILE: orbitdb_many_adds_test.go ================================================ package weshnet import ( "context" crand "crypto/rand" "fmt" "os" "testing" "github.com/libp2p/go-libp2p/core/crypto" orbitdb "berty.tech/go-orbit-db" "berty.tech/weshnet/v2/pkg/cryptoutil" "berty.tech/weshnet/v2/pkg/ipfsutil" "berty.tech/weshnet/v2/pkg/protocoltypes" ) func TestAdd(t *testing.T) { amount := 20 // speeding up tests, 2000 takes ~25 seconds ctx, cancel := context.WithCancel(context.Background()) defer cancel() ipfs := ipfsutil.TestingCoreAPI(ctx, t) dir := "./orbitdb/benchmarks" defer os.RemoveAll(dir) orbit, err := orbitdb.NewOrbitDB(ctx, ipfs.API(), &orbitdb.NewOrbitDBOptions{Directory: &dir}) if err != nil { t.Fatal(err) } defer orbit.Close() if err := orbit.RegisterAccessControllerType(NewSimpleAccessController); err != nil { t.Fatal(err) } sigk, _, err := crypto.GenerateEd25519Key(crand.Reader) if err != nil { t.Fatal(err) } ks := &BertySignedKeyStore{} err = ks.SetKey(sigk) if err != nil { t.Fatal(err) } sigkB, err := cryptoutil.SeedFromEd25519PrivateKey(sigk) if err != nil { t.Fatal(err) } pubkB, err := sigk.GetPublic().Raw() if err != nil { t.Fatal(err) } g := &protocoltypes.Group{PublicKey: pubkB, Secret: sigkB} replicate := false opts, err := DefaultOrbitDBOptions(g, &orbitdb.CreateDBOptions{Replicate: &replicate}, ks, "log", GroupOpenModeWrite) if err != nil { t.Fatal(err) } db, err := orbit.Log(ctx, "DemoLog", opts) if err != nil { t.Fatal(err) } defer db.Drop() defer db.Close() for n := 0; n < amount; n++ { if _, err := db.Add(ctx, []byte(fmt.Sprintf("%d", n))); err != nil { t.Fatal(err) } } } ================================================ FILE: orbitdb_signed_entry_accesscontroller.go ================================================ package weshnet import ( "context" "encoding/json" "sync" cid "github.com/ipfs/go-cid" mh "github.com/multiformats/go-multihash" "github.com/pkg/errors" "go.uber.org/zap" logac "berty.tech/go-ipfs-log/accesscontroller" "berty.tech/go-ipfs-log/identityprovider" "berty.tech/go-orbit-db/accesscontroller" "berty.tech/go-orbit-db/iface" "berty.tech/weshnet/v2/pkg/errcode" ) type simpleAccessController struct { allowedKeys map[string][]string logger *zap.Logger lock sync.RWMutex } func (o *simpleAccessController) SetLogger(logger *zap.Logger) { o.lock.Lock() defer o.lock.Unlock() o.logger = logger } func (o *simpleAccessController) Logger() *zap.Logger { o.lock.RLock() defer o.lock.RUnlock() return o.logger } //nolint:revive func (o *simpleAccessController) Grant(ctx context.Context, capability string, keyID string) error { return nil } //nolint:revive func (o *simpleAccessController) Revoke(ctx context.Context, capability string, keyID string) error { return nil } //nolint:revive func (o *simpleAccessController) Load(ctx context.Context, address string) error { return nil } func simpleAccessControllerCID(allowedKeys map[string][]string) (cid.Cid, error) { d, err := json.Marshal(allowedKeys) if err != nil { return cid.Undef, errcode.ErrCode_ErrInvalidInput.Wrap(err) } c, err := cid.Prefix{ Version: 1, Codec: cid.Raw, MhType: mh.SHA2_256, MhLength: -1, }.Sum(d) if err != nil { return cid.Undef, errcode.ErrCode_ErrInvalidInput.Wrap(err) } return c, nil } func (o *simpleAccessController) Save(context.Context) (accesscontroller.ManifestParams, error) { c, err := simpleAccessControllerCID(o.allowedKeys) if err != nil { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(err) } return accesscontroller.NewManifestParams(c, true, "simple"), nil } func (o *simpleAccessController) Close() error { return nil } func (o *simpleAccessController) Type() string { return "bertysimple" } func (o *simpleAccessController) GetAuthorizedByRole(role string) ([]string, error) { return o.allowedKeys[role], nil } func (o *simpleAccessController) CanAppend(e logac.LogEntry, _ identityprovider.Interface, _ accesscontroller.CanAppendAdditionalContext) error { for _, id := range o.allowedKeys["write"] { if e.GetIdentity().ID == id || id == "*" { return nil } } return errors.New("not allowed to write entry") } // NewSimpleAccessController Returns a non configurable access controller func NewSimpleAccessController(_ context.Context, _ iface.BaseOrbitDB, params accesscontroller.ManifestParams, options ...accesscontroller.Option) (accesscontroller.Interface, error) { if params == nil { return &simpleAccessController{}, errors.New("an options object is required") } ac := &simpleAccessController{ allowedKeys: params.GetAllAccess(), } for _, o := range options { o(ac) } return ac, nil } var _ accesscontroller.Interface = &simpleAccessController{} ================================================ FILE: orbitdb_signed_entry_identity_provider.go ================================================ package weshnet import ( "context" "github.com/libp2p/go-libp2p/core/crypto" "berty.tech/go-ipfs-log/identityprovider" "berty.tech/weshnet/v2/pkg/errcode" ) const ( identityGroupIDKey = "group_id" storeTypeKey = "store_type" identityType = "betry_group_entry" ) type bertySignedIdentityProvider struct { keyStore *BertySignedKeyStore } func (b *bertySignedIdentityProvider) UnmarshalPublicKey(data []byte) (crypto.PubKey, error) { return crypto.UnmarshalPublicKey(data) } func (b *bertySignedIdentityProvider) GetID(_ context.Context, opts *identityprovider.CreateIdentityOptions) (string, error) { return opts.ID, nil } //nolint:revive func (b *bertySignedIdentityProvider) SignIdentity(ctx context.Context, data []byte, id string) ([]byte, error) { return nil, nil } func (b *bertySignedIdentityProvider) GetType() string { return identityType } func (b *bertySignedIdentityProvider) VerifyIdentity(*identityprovider.Identity) error { return nil } func (b *bertySignedIdentityProvider) Sign(ctx context.Context, identity *identityprovider.Identity, bytes []byte) ([]byte, error) { key, err := b.keyStore.GetKey(ctx, identity.ID) if err != nil { return nil, errcode.ErrCode_TODO.Wrap(err) } sig, err := key.Sign(bytes) if err != nil { return nil, errcode.ErrCode_TODO.Wrap(err) } return sig, nil } func (b *bertySignedIdentityProvider) signID(ctx context.Context, id string) (crypto.PubKey, []byte, error) { privKey, err := b.keyStore.GetKey(ctx, id) if err != nil { return nil, nil, err } idSignature, err := b.keyStore.Sign(privKey, []byte(id)) if err != nil { return nil, nil, err } return privKey.GetPublic(), idSignature, nil } func (b *bertySignedIdentityProvider) createIdentity(ctx context.Context, options *identityprovider.CreateIdentityOptions) (*identityprovider.Identity, error) { id, err := b.GetID(ctx, options) if err != nil { return nil, err } publicKey, idSignature, err := b.signID(ctx, id) if err != nil { return nil, err } publicKeyRaw, err := publicKey.Raw() if err != nil { return nil, err } publicKeyBytes, err := crypto.MarshalPublicKey(publicKey) if err != nil { return nil, err } pubKeyIDSignature, err := b.SignIdentity(ctx, append(publicKeyRaw, idSignature...), options.ID) if err != nil { return nil, err } return &identityprovider.Identity{ ID: id, PublicKey: publicKeyBytes, Signatures: &identityprovider.IdentitySignature{ ID: idSignature, PublicKey: pubKeyIDSignature, }, Type: b.GetType(), Provider: b, }, nil } var _ identityprovider.Interface = (*bertySignedIdentityProvider)(nil) ================================================ FILE: orbitdb_signed_entry_keystore.go ================================================ package weshnet import ( "context" "encoding/hex" "sync" "github.com/libp2p/go-libp2p/core/crypto" "berty.tech/go-ipfs-log/keystore" "berty.tech/weshnet/v2/pkg/errcode" ) type BertySignedKeyStore struct { sync.Map } func (s *BertySignedKeyStore) SetKey(pk crypto.PrivKey) error { pubKeyBytes, err := pk.GetPublic().Raw() if err != nil { return errcode.ErrCode_TODO.Wrap(err) } keyID := hex.EncodeToString(pubKeyBytes) s.Store(keyID, pk) return nil } func (s *BertySignedKeyStore) HasKey(_ context.Context, id string) (bool, error) { _, ok := s.Load(id) return ok, nil } func (s *BertySignedKeyStore) CreateKey(ctx context.Context, id string) (crypto.PrivKey, error) { return s.GetKey(ctx, id) } func (s *BertySignedKeyStore) GetKey(_ context.Context, id string) (crypto.PrivKey, error) { if privKey, ok := s.Load(id); ok { if pk, ok := privKey.(crypto.PrivKey); ok { return pk, nil } } return nil, errcode.ErrCode_ErrGroupMemberUnknownGroupID } func (s *BertySignedKeyStore) Sign(privKey crypto.PrivKey, bytes []byte) ([]byte, error) { return privKey.Sign(bytes) } func (s *BertySignedKeyStore) Verify(signature []byte, publicKey crypto.PubKey, data []byte) error { ok, err := publicKey.Verify(data, signature) if err != nil { return err } if !ok { return errcode.ErrCode_ErrGroupMemberLogEventSignature } return nil } func (s *BertySignedKeyStore) getIdentityProvider() *bertySignedIdentityProvider { return &bertySignedIdentityProvider{ keyStore: s, } } var _ keystore.Interface = (*BertySignedKeyStore)(nil) ================================================ FILE: orbitdb_test.go ================================================ package weshnet import ( "context" "encoding/hex" "os" "strings" "testing" "time" datastore "github.com/ipfs/go-datastore" sync_ds "github.com/ipfs/go-datastore/sync" mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap" "berty.tech/weshnet/v2/internal/datastoreutil" "berty.tech/weshnet/v2/pkg/ipfsutil" "berty.tech/weshnet/v2/pkg/protocoltypes" "berty.tech/weshnet/v2/pkg/testutil" "berty.tech/weshnet/v2/pkg/tinder" ) func TestDifferentStores(t *testing.T) { testutil.FilterSpeed(t, testutil.Slow) logger, cleanup := testutil.Logger(t) defer cleanup() ctx, cancel := context.WithCancel(context.Background()) defer cancel() mn := mocknet.New() defer mn.Close() ipfsOpts := &ipfsutil.TestingAPIOpts{ Logger: logger, Mocknet: mn, DiscoveryServer: tinder.NewMockDriverServer(), } pathBase, err := os.MkdirTemp("", "odb_manyaddstest") if err != nil { t.Fatal(err) } require.NoError(t, mn.ConnectAllButSelf()) baseDS, err := GetRootDatastoreForPath(pathBase, nil, nil, zap.NewNop()) require.NoError(t, err) baseDS = sync_ds.MutexWrap(baseDS) defer testutil.Close(t, baseDS) api1 := ipfsutil.TestingCoreAPIUsingMockNet(ctx, t, ipfsOpts) odb1 := NewTestOrbitDB(ctx, t, logger, api1, datastoreutil.NewNamespacedDatastore(baseDS, datastore.NewKey("peer1"))) defer testutil.Close(t, odb1) api2 := ipfsutil.TestingCoreAPIUsingMockNet(ctx, t, ipfsOpts) odb2 := NewTestOrbitDB(ctx, t, logger, api2, datastoreutil.NewNamespacedDatastore(baseDS, datastore.NewKey("peer2"))) defer testutil.Close(t, odb2) err = mn.LinkAll() require.NoError(t, err) err = mn.ConnectAllButSelf() require.NoError(t, err) gA, _, err := NewGroupMultiMember() require.NoError(t, err) gB, _, err := NewGroupMultiMember() require.NoError(t, err) assert.NotEqual(t, gA.PublicKey, gB.PublicKey) g1a, err := odb1.OpenGroup(ctx, gA, nil) require.NoError(t, err) defer g1a.Close() g2a, err := odb2.OpenGroup(ctx, gA, nil) require.NoError(t, err) defer g2a.Close() g1b, err := odb1.OpenGroup(ctx, gB, nil) require.NoError(t, err) defer g1b.Close() g2b, err := odb2.OpenGroup(ctx, gB, nil) require.NoError(t, err) defer g2b.Close() assert.Equal(t, g1a.MetadataStore().Address().String(), g2a.MetadataStore().Address().String()) assert.Equal(t, g1b.MetadataStore().Address().String(), g2b.MetadataStore().Address().String()) assert.NotEqual(t, g1a.MetadataStore().Address().String(), g1a.MessageStore().Address().String()) assert.NotEqual(t, g1a.MetadataStore().Address().String(), g1b.MetadataStore().Address().String()) authorized1, err := g1a.MetadataStore().AccessController().GetAuthorizedByRole("write") require.NoError(t, err) authorized2, err := g1a.MetadataStore().AccessController().GetAuthorizedByRole("write") require.NoError(t, err) assert.Equal(t, strings.Join(authorized1, ","), strings.Join(authorized2, ",")) pk1, err := g1a.MetadataStore().Identity().GetPublicKey() require.NoError(t, err) pk2, err := g2a.MetadataStore().Identity().GetPublicKey() require.NoError(t, err) require.True(t, pk1.Equals(pk2)) rawPK, err := pk1.Raw() require.NoError(t, err) require.Equal(t, hex.EncodeToString(rawPK), authorized1[0]) _, err = g1a.MetadataStore().SendAppMetadata(ctx, []byte("From 1 - 1")) require.NoError(t, err) _, err = g2a.MetadataStore().SendAppMetadata(ctx, []byte("From 2 - 1")) require.NoError(t, err) _, err = g1b.MetadataStore().SendAppMetadata(ctx, []byte("From 1 - 2")) require.NoError(t, err) _, err = g2b.MetadataStore().SendAppMetadata(ctx, []byte("From 2 - 2")) require.NoError(t, err) _, err = g1b.MetadataStore().SendAppMetadata(ctx, []byte("From 1 - 3")) require.NoError(t, err) _, err = g2b.MetadataStore().SendAppMetadata(ctx, []byte("From 2 - 3")) require.NoError(t, err) { var err error var cc <-chan *protocoltypes.GroupMetadataEvent var ops []*protocoltypes.GroupMetadataPayloadSent assert.Eventually(t, func() bool { cc, err = g1a.MetadataStore().ListEvents(ctx, nil, nil, false) ops = testutil.TestFilterGroupMetadataPayloadSent(t, cc) return len(ops) == 2 }, time.Second*2, time.Millisecond*100, "have: %d, want: %d", len(ops), 2) require.NoError(t, err) assert.Eventually(t, func() bool { cc, err = g2a.MetadataStore().ListEvents(ctx, nil, nil, false) ops = testutil.TestFilterGroupMetadataPayloadSent(t, cc) return len(ops) == 2 }, time.Second*2, time.Millisecond*100, "have: %d, want: %d", len(ops), 2) require.NoError(t, err) assert.Eventually(t, func() bool { cc, err = g1b.MetadataStore().ListEvents(ctx, nil, nil, false) ops = testutil.TestFilterGroupMetadataPayloadSent(t, cc) return len(ops) == 4 }, time.Second*2, time.Millisecond*100, "have: %d, want: %d", len(ops), 5) require.NoError(t, err) assert.Eventually(t, func() bool { cc, err = g2b.MetadataStore().ListEvents(ctx, nil, nil, false) ops = testutil.TestFilterGroupMetadataPayloadSent(t, cc) return len(ops) == 4 }, time.Second*2, time.Millisecond*100, "have: %d, want: %d", len(ops), 4) require.NoError(t, err) } } ================================================ FILE: orbitdb_utils_test.go ================================================ package weshnet import ( "context" "sync" "testing" "github.com/libp2p/go-libp2p/core/crypto" "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" "berty.tech/weshnet/v2/pkg/protocoltypes" ) func inviteAllPeersToGroup(ctx context.Context, t *testing.T, peers []*mockedPeer, groupSK crypto.PrivKey) { t.Helper() wg := sync.WaitGroup{} wg.Add(len(peers)) errChan := make(chan error, len(peers)) for i, p := range peers { sub, err := p.GC.MetadataStore().EventBus().Subscribe(new(*protocoltypes.GroupMetadataEvent)) require.NoError(t, err) go func(p *mockedPeer, peerIndex int) { defer sub.Close() defer wg.Done() eventReceived := 0 for e := range sub.Out() { evt := e.(*protocoltypes.GroupMetadataEvent) if evt.Metadata.EventType != protocoltypes.EventType_EventTypeGroupMemberDeviceAdded { continue } memdev := &protocoltypes.GroupMemberDeviceAdded{} if err := proto.Unmarshal(evt.Event, memdev); err != nil { errChan <- err return } eventReceived++ if eventReceived == len(peers) { return } } }(p, i) } for i, p := range peers { _, err := p.GC.MetadataStore().AddDeviceToGroup(ctx) require.NoError(t, err) if i == 0 { _, err := p.GC.MetadataStore().ClaimGroupOwnership(ctx, groupSK) require.NoError(t, err) } } // Wait for all events to be received in all peers's member log (or timeout) wg.Wait() close(errChan) for err := range errChan { t.Fatal(err) } } func waitForBertyEventType(ctx context.Context, t *testing.T, ms *MetadataStore, eventType protocoltypes.EventType, eventCount int, done chan struct{}) { t.Helper() ctx, cancel := context.WithCancel(ctx) defer cancel() handledEvents := map[string]struct{}{} sub, err := ms.EventBus().Subscribe(new(*protocoltypes.GroupMetadataEvent)) require.NoError(t, err) defer sub.Close() for { var e any select { case e = <-sub.Out(): case <-ctx.Done(): return } switch evt := e.(type) { case *protocoltypes.GroupMetadataEvent: if evt.Metadata.EventType != eventType { continue } eID := string(evt.EventContext.Id) if _, ok := handledEvents[eID]; ok { continue } handledEvents[eID] = struct{}{} e := &protocoltypes.GroupDeviceChainKeyAdded{} if err := proto.Unmarshal(evt.Event, e); err != nil { t.Fatalf(" err: %+v\n", err.Error()) } // fmt.Println(string(e.DevicePK), string(e.DestMemberPK)) eventCount-- if eventCount == 0 { done <- struct{}{} } else { // fmt.Println(eventCount, "more to go") } } } } ================================================ FILE: pkg/androidnearby/bridge_android.go ================================================ //go:build android && !noproximitytransport package androidnearby import ( "go.uber.org/zap" proximity "berty.tech/weshnet/v2/pkg/proximitytransport" ) // Supported is used by main package as default value for enable this driver. // While UI actually enable or not the Java Android Nearby driver. // TODO: remove this when UI will be able to handle this for the first App launching. const Supported = true // Noop implementation for Android // Real driver is given from Java directly here: berty/js/android/app/src/main/java/tech/berty/gobridge/nearby func NewDriver(logger *zap.Logger) proximity.ProximityDriver { logger = logger.Named("Nearby") logger.Info("NewDriver(): Java driver not found") return proximity.NewNoopProximityDriver(ProtocolCode, ProtocolName, DefaultAddr) } ================================================ FILE: pkg/androidnearby/bridge_unsupported.go ================================================ //go:build !android || noproximitytransport package androidnearby import ( "go.uber.org/zap" proximity "berty.tech/weshnet/v2/pkg/proximitytransport" ) const Supported = false // Noop implementation for platform that are not Darwin func NewDriver(logger *zap.Logger) proximity.ProximityDriver { logger = logger.Named("Nearby") logger.Info("NewDriver(): incompatible system") return proximity.NewNoopProximityDriver(ProtocolCode, ProtocolName, DefaultAddr) } ================================================ FILE: pkg/androidnearby/const.go ================================================ package androidnearby const ( DefaultAddr = "/nearby/Qmeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" ProtocolCode = 0x0044 ProtocolName = "nearby" ) ================================================ FILE: pkg/androidnearby/example_test.go ================================================ package androidnearby_test ================================================ FILE: pkg/androidnearby/init.go ================================================ package androidnearby import ( ma "github.com/multiformats/go-multiaddr" ) // Add MC to the list of libp2p's multiaddr protocols // FIXME: remove this init func init() { // nolint:gochecknoinits err := ma.AddProtocol(newProtocol()) if err != nil { panic(err) } } ================================================ FILE: pkg/androidnearby/multiaddr.go ================================================ package androidnearby import ( peer "github.com/libp2p/go-libp2p/core/peer" ma "github.com/multiformats/go-multiaddr" ) func newProtocol() ma.Protocol { transcoderMC := ma.NewTranscoderFromFunctions(mcStB, mcBtS, mcVal) return ma.Protocol{ Name: ProtocolName, Code: ProtocolCode, VCode: ma.CodeToVarint(ProtocolCode), Size: -1, Path: false, Transcoder: transcoderMC, } } func mcStB(s string) ([]byte, error) { _, err := peer.Decode(s) if err != nil { return nil, err } return []byte(s), nil } func mcBtS(b []byte) (string, error) { _, err := peer.Decode(string(b)) if err != nil { return "", err } return string(b), nil } func mcVal(b []byte) error { _, err := peer.Decode(string(b)) return err } ================================================ FILE: pkg/bertyvcissuer/client.go ================================================ package bertyvcissuer import ( "context" "crypto" crand "crypto/rand" "encoding/base64" "encoding/json" "fmt" "io" "net/http" "net/url" "time" "github.com/hyperledger/aries-framework-go/pkg/doc/verifiable" "github.com/piprate/json-gold/ld" "berty.tech/weshnet/v2/pkg/errcode" "berty.tech/weshnet/v2/pkg/verifiablecredstypes" ) const DefaultRedirectURI = "berty://vc" type Client struct { serverRoot string redirectURI string httpClient *http.Client state string bertyURL string } func NewClient(serverRoot string) *Client { return &Client{ serverRoot: serverRoot, redirectURI: DefaultRedirectURI, httpClient: http.DefaultClient, } } func (c *Client) Init(ctx context.Context, bertyURL string, accountPriv crypto.Signer) (string, error) { c.state = base64.RawURLEncoding.EncodeToString([]byte(time.Now().String())) c.bertyURL = bertyURL req, 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) if err != nil { return "", errcode.ErrCode_ErrInternal.Wrap(err) } res, err := c.httpClient.Do(req) if err != nil { return "", errcode.ErrCode_ErrStreamRead.Wrap(err) } resBytes, err := io.ReadAll(res.Body) if err != nil { return "", errcode.ErrCode_ErrStreamRead.Wrap(err) } _ = res.Body.Close() if res.StatusCode != http.StatusOK { return "", errcode.ErrCode_ErrInternal.Wrap(fmt.Errorf(string(resBytes))) } challengeStruct := &verifiablecredstypes.AccountCryptoChallenge{} err = json.Unmarshal(resBytes, challengeStruct) if err != nil { return "", errcode.ErrCode_ErrDeserialization.Wrap(err) } challenge, err := base64.URLEncoding.DecodeString(challengeStruct.Challenge) if err != nil { return "", errcode.ErrCode_ErrDeserialization.Wrap(err) } challengeSig, err := accountPriv.Sign(crand.Reader, challenge, crypto.Hash(0)) if err != nil { return "", errcode.ErrCode_ErrCryptoSignature.Wrap(err) } return fmt.Sprintf("%s/%s?&%s=%s&%s=%s", c.serverRoot, PathAuthenticate, ParamChallenge, challengeStruct.Challenge, ParamChallengeSig, base64.URLEncoding.EncodeToString(challengeSig)), nil } func (c *Client) Complete(uri string) (string, string, *verifiable.Credential, error) { parsedURI, err := url.Parse(uri) if err != nil { return "", "", nil, errcode.ErrCode_ErrInvalidInput.Wrap(err) } if parsedURI.Query().Get(ParamState) != c.state { return "", "", nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("unexpected state value")) } credentialsStr := parsedURI.Query().Get(ParamCredentials) if len(credentialsStr) == 0 { return "", "", nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("missing credentials value")) } credentials, err := base64.StdEncoding.DecodeString(credentialsStr) if err != nil { return "", "", nil, errcode.ErrCode_ErrDeserialization.Wrap(err) } parsedCredential, err := verifiable.ParseCredential( credentials, verifiable.WithPublicKeyFetcher(EmbeddedPublicKeyFetcher), verifiable.WithJSONLDDocumentLoader(ld.NewDefaultDocumentLoader(http.DefaultClient)), ) if err != nil { return "", "", nil, errcode.ErrCode_ErrDeserialization.Wrap(err) } if c.bertyURL != parsedCredential.ID { return "", "", nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("credential is not delivered for the current berty url (%s != %s)", c.bertyURL, parsedCredential.ID)) } identifier, err := ExtractSubjectFromVC(parsedCredential) if err != nil { return "", "", nil, errcode.ErrCode_ErrInvalidInput.Wrap(err) } return string(credentials), identifier, parsedCredential, nil } func ExtractSubjectFromVC(credential *verifiable.Credential) (string, error) { if credential.Subject == nil { return "", errcode.ErrCode_ErrNotFound } if subjectList, ok := credential.Subject.([]verifiable.Subject); ok { if len(subjectList) == 0 { return "", errcode.ErrCode_ErrNotFound } return subjectList[0].ID, nil } else if subject, ok := credential.Subject.(string); ok && subject != "" { return subject, nil } return "", errcode.ErrCode_ErrNotFound } ================================================ FILE: pkg/bertyvcissuer/urls.go ================================================ package bertyvcissuer import ( "encoding/base64" "fmt" ) const ( PathChallenge = "/challenge" PathAuthenticate = "/authenticate" PathProof = "/proof" ParamBertyID = "berty_id" ParamState = "state" ParamRedirectURI = "redirect_uri" ParamChallenge = "challenge" ParamChallengeSig = "challenge_sig" ParamCode = "code" ParamContext = "context" ParamCredentials = "credentials" ParamIdentifier = "identifier" ) func MakeAuthenticateURL(serverBaseRoot, flowCtxStr string) string { return fmt.Sprintf("%s%s?%s=%s", serverBaseRoot, PathAuthenticate, ParamContext, flowCtxStr) } func MakeProofURL(serverBaseRoot, flowCtxStr string) string { return fmt.Sprintf("%s%s?%s=%s", serverBaseRoot, PathProof, ParamContext, flowCtxStr) } func MakeRedirectSuccessURI(redirectURI, state string, credentials []byte) string { return fmt.Sprintf("%s%s?%s=%s&%s=%s", redirectURI, PathProof, ParamState, state, ParamCredentials, base64.URLEncoding.EncodeToString(credentials)) } ================================================ FILE: pkg/bertyvcissuer/verifiable_public_key_fetcher.go ================================================ package bertyvcissuer import ( "crypto/ed25519" "fmt" "slices" "strings" "github.com/hyperledger/aries-framework-go/pkg/doc/signature/verifier" "github.com/hyperledger/aries-framework-go/pkg/kms" "github.com/multiformats/go-multibase" "berty.tech/weshnet/v2/pkg/errcode" ) func embeddedPublicKeyFetcher(issuerID string, allowList []string) (*verifier.PublicKey, error) { if !strings.HasPrefix(issuerID, "did:key:z6Mk") { return nil, fmt.Errorf("unexpected key format") } if len(allowList) > 0 { found := slices.Contains(allowList, issuerID) if !found { return nil, errcode.ErrCode_ErrServicesDirectoryInvalidVerifiedCredentialID.Wrap(fmt.Errorf("issuer is not allowed")) } } _, rawData, err := multibase.Decode(issuerID[8:]) if err != nil { return nil, err } if len(rawData) != ed25519.PublicKeySize+2 { return nil, errcode.ErrCode_ErrInvalidInput } return &verifier.PublicKey{ Type: kms.ED25519, Value: rawData[2:], JWK: nil, }, nil } // nolint:revive func EmbeddedPublicKeyFetcher(issuerID, keyID string) (*verifier.PublicKey, error) { return embeddedPublicKeyFetcher(issuerID, nil) } // nolint:revive func EmbeddedPublicKeyFetcherAllowList(allowList []string) func(issuerID, keyID string) (*verifier.PublicKey, error) { return func(issuerID, keyID string) (*verifier.PublicKey, error) { return embeddedPublicKeyFetcher(issuerID, allowList) } } ================================================ FILE: pkg/ble-driver/BertyDevice_darwin.h ================================================ // // BertyDevice.h // ble // // Created by sacha on 03/06/2019. // Copyright © 2019 berty. All rights reserved. // #import #import #import "ConnectedPeer.h" #import "CircularQueue.h" #import "BleQueue.h" #import "Logger.h" #import "CountDownLatch_darwin.h" NS_ASSUME_NONNULL_BEGIN @class BleManager; typedef void (^BertyDeviceConnectCallbackBlockType)(BertyDevice * __nullable, NSError * __nullable); typedef void (^BertyDeviceServiceCallbackBlockType)(NSArray * __nullable, NSError * __nullable); typedef void (^BertyDeviceWriteCallbackBlockType)(NSError * __nullable); #define _BERTY_ON_D_THREAD(block) dispatch_async(self.dQueue, block) #define _BERTY_ON_W_THREAD(block) dispatch_async(self.writeQueue, block) @interface BertyDevice : NSObject @property (nonatomic, strong, nonnull) Logger *logger; @property (nonatomic, strong, nonnull) NSString *name; @property (nonatomic, strong, nonnull) NSDictionary *serviceDict; @property (nonatomic, strong, nullable) CBPeripheral *peripheral; @property (nonatomic, strong, nonnull) NSString *serverSideIdentifier; @property (nonatomic, strong, nonnull) NSString *clientSideIdentifier; @property (nonatomic, assign, nullable) BleManager *manager; @property (nonatomic, strong, nonnull) BleQueue *connectionQ; @property (nonatomic, strong, nonnull) BleQueue *writeQ; @property (nonatomic, strong, nonnull) BleQueue *readQ; @property (nonatomic, strong, nullable) NSObject *writerLatch; @property (nonatomic, strong, nullable) CBCharacteristic *peerIDCharacteristic; @property (nonatomic, strong, nullable) CBCharacteristic *writerCharacteristic; @property (nonatomic, strong, nonnull) NSDictionary* characteristicHandlers; @property (nonatomic, strong, nonnull) NSDictionary* characteristicData; @property (nonatomic, strong, nullable) NSData *remainingData; @property (nonatomic, strong, nullable) NSString *remotePeerID; @property (readwrite) int psm; @property (nonatomic, strong, nullable) ConnectedPeer *peer; @property (nonatomic, strong, nonnull) CBCentral *cbCentral; @property (nonatomic, strong, nonnull) CircularQueue *dataCache; @property (readwrite) BOOL isDisconnecting; - (instancetype __nullable)initWithIdentifier:(NSString *__nonnull)identifier logger:(Logger *__nonnull)logger central:(BleManager *__nonnull)manager asClient:(BOOL)client; - (instancetype __nullable)initWithPeripheral:(CBPeripheral *__nonnull)peripheral logger:(Logger *__nonnull)logger central:(BleManager *__nonnull)manager withName:(NSString *__nonnull)name; - (void)closeBertyDevice; - (BOOL)writeToCharacteristic:(NSData *__nonnull)data forCharacteristic:(CBCharacteristic *__nonnull)characteristic withEOD:(BOOL)eod; - (void)handshake; - (void)handleConnect:(NSError * __nullable)error; - (void)connectWithOptions:(NSDictionary * __nullable)options; - (NSString *__nonnull)getIdentifier; - (void)flushCache; @end API_AVAILABLE(ios(11.0)) @interface BertyDevice() @property (strong, nullable) NSThread *l2capThread; @property (strong, nullable) CBL2CAPChannel *l2capChannel; @property (strong, nullable) NSData *l2capWriteData; @property (readwrite) NSInteger l2capWriteIndex; @property (readwrite) BOOL useL2cap; @property (readwrite) BOOL l2capClientHandshakeRunning; @property (readwrite) BOOL l2capServerHandshakeRunning; @property (strong, nullable) CountDownLatch *l2capHandshakeLatch; @property (readwrite) BOOL l2capHandshakeStepStatus; @property (copy, nullable) dispatch_block_t l2capHandshakeBlock; @property (strong, nullable) NSMutableData *l2capHandshakeData; @property (strong, nullable) NSMutableData *l2capHandshakeRecvData; @property (readwrite) NSUInteger l2capHandshakeRecvDataLen; - (BOOL)l2capWrite:(NSData *__nonnull)data; @end NS_ASSUME_NONNULL_END ================================================ FILE: pkg/ble-driver/BertyDevice_darwin.m ================================================ // +build darwin,!noproximitytransport // // BertyDevice.m // ble // // Created by sacha on 03/06/2019. // Copyright © 2019 berty. All rights reserved. // #import "BertyDevice_darwin.h" #import "BleManager_darwin.h" extern unsigned short handlePeerFound(char *, char *); extern void receiveFromDevice(char *, void *, int); static NSString* const __nonnull EOD = @"EOD"; static const int L2CAP_BUFFER = 4096; static const int L2CAP_HANDSHAKE_DATA = 1024; CBService *getService(NSArray *services, NSString *uuid) { CBService *result = nil; for (CBService *service in services) { if ([service.UUID.UUIDString containsString:uuid] != NSNotFound) { result = service; } } return result; } @implementation BertyDevice - (instancetype)initWithPeripheral:(CBPeripheral *)peripheral logger:(Logger *__nonnull)logger central:(BleManager *)manager withName:(NSString *__nonnull)name { self = [self initWithIdentifier:[peripheral.identifier UUIDString] logger:logger central:manager asClient:TRUE]; if (self) { _peripheral = [peripheral retain]; _name = name; } return self; } - (instancetype)initWithIdentifier:(NSString *)identifier logger:(Logger *__nonnull)logger central:(BleManager *)manager asClient:(BOOL)client{ self = [super init]; if (self) { if (client) { _clientSideIdentifier = [identifier retain]; } else { _serverSideIdentifier = [identifier retain]; } _logger = [logger retain]; _peripheral = nil; _manager = manager; _remotePeerID = nil; _psm = 0; _connectionQ = [[BleQueue alloc] init: dispatch_get_main_queue() logger:logger]; _writeQ = [[BleQueue alloc] init: dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) logger:logger]; _readQ = [[BleQueue alloc] init: dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) logger:logger]; BOOL (^peerIDHandler)(NSData *data) = ^BOOL(NSData *data) { return [self handlePeerID:data]; }; BOOL (^writeHandler)(NSData *data) = ^BOOL(NSData *data) { return [self handleIncomingData:data]; }; _characteristicHandlers = [@{ [BleManager.writerUUID UUIDString]: [[writeHandler copy] autorelease], [BleManager.peerUUID UUIDString]: [[peerIDHandler copy] autorelease], } retain]; _characteristicData = [@{ [BleManager.writerUUID UUIDString]: [NSMutableData data], [BleManager.peerUUID UUIDString]: [NSMutableData data], } retain]; // put inside incoming message arrived before handshake is completed _dataCache = [[CircularQueue alloc] initWithCapacity:10]; _writerLatch = [[NSObject alloc] init]; } return self; } - (void)dealloc { [_logger release]; [_clientSideIdentifier release]; [_serverSideIdentifier release]; [_peripheral release]; _manager = nil; [_remotePeerID release]; [_connectionQ release]; [_writeQ release]; [_readQ release]; [_characteristicHandlers release]; [_characteristicData release]; [_dataCache release]; [_writerLatch release]; [super dealloc]; } - (NSString *__nonnull)getIdentifier { if (self.clientSideIdentifier != nil) { return self.clientSideIdentifier; } return self.serverSideIdentifier; } - (void)closeL2cap { [self.logger d:@"closeL2cap: device=%@", [self.logger SensitiveNSObject:[self getIdentifier]]]; if (self.l2capChannel != nil) { [self.l2capChannel.inputStream close]; [self.l2capChannel.outputStream close]; if (self.l2capThread != nil) { [self.l2capThread cancel]; [self.l2capThread release]; self.l2capThread = nil; } } } - (void)closeBertyDevice { @synchronized (self) { [self.logger d:@"closeBertyDevice: device=%@", [self.logger SensitiveNSObject:[self getIdentifier]]]; if (!self.isDisconnecting) { self.isDisconnecting = TRUE; [self.connectionQ clear]; [self.writeQ clear]; [self.readQ clear]; [self closeL2cap]; if (self.peer != nil) { [self.manager.peerManager unregisterDevice:self]; self.peer = nil; } } else { [self.logger d:@"closeBertyDevice: device=%@ is already disconnecting", [self.logger SensitiveNSObject:[self getIdentifier]]]; } self.isDisconnecting = FALSE; } } - (BOOL)handlePeerID:(NSData *__nonnull)peerIDData { if (self.peer != nil && [self.peer isConnected]) { [self.logger e:@"handlePeerID: device=%@: peer already connected", [self.logger SensitiveNSObject:[self getIdentifier]]]; return FALSE; } NSMutableData *tmpData = [self.characteristicData objectForKey:[BleManager.peerUUID UUIDString]]; if ([peerIDData isEqual:[EOD dataUsingEncoding:NSUTF8StringEncoding]]) { // adding 0 byte unsigned char zeroByte = 0; @synchronized (tmpData) { [tmpData appendBytes:&zeroByte length:1]; } NSString *remotePeerID = [NSString stringWithUTF8String:[tmpData bytes]]; // reset tmpData [tmpData setLength:0]; [self.logger d:@"handlePeerID: device=%@: current peerID=%@, new peerID=%@", [self.logger SensitiveNSObject:[self getIdentifier]], [self.logger SensitiveNSObject:self.remotePeerID], [self.logger SensitiveNSObject:remotePeerID]]; self.remotePeerID = remotePeerID; } else { @synchronized (tmpData) { [self.logger d:@"handlePeerID: device=%@: add to buffer data=%@", [self.logger SensitiveNSObject:[self getIdentifier]], [self.logger SensitiveNSObject:peerIDData]]; [tmpData appendData:peerIDData]; } } return TRUE; } - (BOOL)putIncomingDataInCache:(NSData *__nonnull)data { @try { [self.dataCache offer:data]; } @catch (NSException *e) { [self.logger e:@"putIncomingDataInCache error: device=%@: cannot add data in cache", [self.logger SensitiveNSObject:[self getIdentifier]]]; return FALSE; } return TRUE; } - (BOOL)handleIncomingData:(NSData *__nonnull)data { [self.logger d:@"handleIncomingData called: identifier=%@ len=%lu base64=%@", [self.logger SensitiveNSObject:[self getIdentifier]], [data length], [self.logger SensitiveNSObject:[data base64EncodedStringWithOptions:0]]]; if ([self.logger showSensitiveData]) { [BleManager printLongLog:[BleManager NSDataToHex:data]]; } if (self.l2capClientHandshakeRunning) { [self.l2capHandshakeRecvData appendBytes:data length:[data length]]; if ([self.l2capHandshakeRecvData length] < L2CAP_HANDSHAKE_DATA) { [self.logger d:@"handleIncomingData: device=%@: client handshake received incompleted payload: length=%lu", [self.logger SensitiveNSObject:[self getIdentifier]], [self.l2capHandshakeRecvData length]]; } else if ([self.l2capHandshakeRecvData length] == L2CAP_HANDSHAKE_DATA) { if ([data isEqualToData:self.l2capHandshakeData]) { [self.logger d:@"handleIncomingData: device=%@: client handshake received payload", [self.logger SensitiveNSObject:[self getIdentifier]]]; self.l2capHandshakeStepStatus = TRUE; dispatch_block_cancel(self.l2capHandshakeBlock); [self.l2capHandshakeLatch countDown]; } else { [self.logger e:@"handleIncomingData: device=%@: client handshake received wrong payload", [self.logger SensitiveNSObject:[self getIdentifier]]]; dispatch_block_cancel(self.l2capHandshakeBlock); [self.l2capHandshakeLatch countDown]; [self.manager disconnect:self]; } } else { [self.logger e:@"handleIncomingData: device=%@: client handshake received bigger payload than expected: length=%lu", [self.logger SensitiveNSObject:[self getIdentifier]], [self.l2capHandshakeRecvData length]];; } } else if (self.l2capServerHandshakeRunning) { if (!self.l2capHandshakeStepStatus) { [self.logger d:@"handleIncomingData: device=%@: server handshake received payload, going to write it back", [self.logger SensitiveNSObject:[self getIdentifier]]]; // the server side needs to know when it receives all 1st step data, so it must count data len self.l2capHandshakeRecvDataLen += [data length]; if (self.l2capHandshakeRecvDataLen == L2CAP_HANDSHAKE_DATA) { self.l2capHandshakeStepStatus = TRUE; self.l2capHandshakeRecvDataLen = 0; } if (![self l2capWrite:data]) { [self.logger e:@"handleIncomingData: device=%@: server handshake write error", [self.logger SensitiveNSObject:[self getIdentifier]]]; self.l2capServerHandshakeRunning = FALSE; self.l2capHandshakeStepStatus = FALSE; } } else if ([data isEqualToData:[self.manager.localPID dataUsingEncoding:NSUTF8StringEncoding]]) { [self.logger d:@"handleIncomingData: device=%@: server handshake received second payload", [self.logger SensitiveNSObject:[self getIdentifier]]]; self.l2capServerHandshakeRunning = FALSE; self.useL2cap = TRUE; } else { [self.logger e:@"handleIncomingData: device=%@: server handshake received wrong payload", [self.logger SensitiveNSObject:[self getIdentifier]]]; } } else { if (!self.peer) { [self.logger e:@"handleIncomingData: device=%@: peer not existing", [self.logger SensitiveNSObject:[self getIdentifier]]]; return [self putIncomingDataInCache:data]; } if (![self.peer isConnected]) { [self.logger d:@"handleIncomingData: device=%@: peer not connected, put data in cache", [self.logger SensitiveNSObject:[self getIdentifier]]]; return [self putIncomingDataInCache:data]; } [self.readQ add:^{ BLEBridgeReceiveFromPeer(self.remotePeerID, data); [self.readQ completedTask:nil]; } withCallback:nil withDelay:0]; } return TRUE; } // Need to copy blocks into the heap because writing is async and the handshake function's stack should not be available - (void)handshake { if (![self writeToCharacteristic:[self.manager.localPID dataUsingEncoding:NSUTF8StringEncoding] forCharacteristic:self.peerIDCharacteristic withEOD:TRUE]) { [self.manager disconnect:self]; return ; } if (![self readToCharacteristic:self.peerIDCharacteristic]) { [self.manager disconnect:self]; return ; } [self negotiateL2cap]; if (![self setNotifyValue]) { [self.manager disconnect:self]; } } - (BOOL)setNotifyValue { if (self.peripheral != nil && self.peripheral.state == CBPeripheralStateConnected) { [self.logger d:@"setNotifyValue: going to subscribe to writer notifications"]; [self.writeQ add:^{ if (self.peripheral != nil && self.peripheral.state == CBPeripheralStateConnected) { [self.logger d:@"setNotifyValue: subscribing to writer notifications"]; [self.peripheral setNotifyValue:TRUE forCharacteristic:self.writerCharacteristic]; } } withCallback:nil withDelay:0]; return TRUE; } return FALSE; } - (void)peripheral:(CBPeripheral *)peripheral didModifyServices:(NSArray *)invalidatedServices { CBService *service = getService(invalidatedServices, [BleManager.serviceUUID UUIDString]); if (service == nil) { return; } [self.logger d:@"didModifyServices: device=%@ service=%@", [self.logger SensitiveNSObject:[self getIdentifier]], invalidatedServices]; [self.manager disconnect:self]; } - (void)handleConnect:(NSError *)error { [self.connectionQ completedTask:error]; if (error) { [self.logger e:@"handleConnect error: device=%@ error=%@", [self.logger SensitiveNSObject:[self getIdentifier]], error]; [self.manager disconnect:self]; return; } [self.logger i:@"handleConnect: device=%@: connection successed", [self.logger SensitiveNSObject:[self getIdentifier]]]; [self discoverServices:@[self.manager.serviceUUID]]; } - (void)connectWithOptions:(NSDictionary *)options { [self.logger d:@"connectWithOptions called: device=%@", [self.logger SensitiveNSObject:[self getIdentifier]]]; [self.connectionQ add:^{ [self.logger d:@"connectWithOptions: device=%@: in queue for connecting", [self.logger SensitiveNSObject:[self getIdentifier]]]; [self.manager.cManager connectPeripheral:self.peripheral options:nil]; } withCallback:nil withDelay:0]; } #pragma mark - write functions - (void)flushCache { [self.logger d:@"flushCache called: device=%@", [self.logger SensitiveNSObject:[self getIdentifier]]]; while ([self.dataCache element] != [NSNull null]) { NSData *data = [[self.dataCache poll] retain]; [self.logger d:@"flushCache: device=%@ base64=%@ data=%@", [self.logger SensitiveNSObject:[self getIdentifier]], [self.logger SensitiveNSObject:[data base64EncodedStringWithOptions:0]], [self.logger SensitiveNSObject:[BleManager NSDataToHex:data]]]; BLEBridgeReceiveFromPeer(self.remotePeerID, data); [data release]; } } - (NSData *)getDataToSend { NSData *result = nil; if (self.remainingData == nil || self.remainingData.length <= 0) { return result; } NSUInteger chunckSize = self.remainingData.length > [self.peripheral maximumWriteValueLengthForType:CBCharacteristicWriteWithResponse] ? [self.peripheral maximumWriteValueLengthForType:CBCharacteristicWriteWithResponse] : self.remainingData.length; result = [NSData dataWithBytes:[self.remainingData bytes] length:chunckSize]; if (self.remainingData.length <= chunckSize) { self.remainingData = nil; } else { self.remainingData = [[NSData alloc] initWithBytes:[self.remainingData bytes] + chunckSize length:[self.remainingData length] - chunckSize]; } return result; } - (BOOL)writeToCharacteristic:(NSData *)data forCharacteristic:(CBCharacteristic *)characteristic withEOD:(BOOL)eod { @synchronized (self.writerLatch) { [self.logger d:@"writeToCharacteristic called: device=%@ base64=%@", [self.logger SensitiveNSObject:[self getIdentifier]], [self.logger SensitiveNSObject:[data base64EncodedStringWithOptions:0]]]; if ([self.logger showSensitiveData]) { [BleManager printLongLog:[BleManager NSDataToHex:data]]; } __block BOOL success = FALSE; NSData *toSend = nil; self.remainingData = data; while (self.remainingData.length > 0) { if (self.peripheral != nil && self.peripheral.state == CBPeripheralStateConnected) { toSend = [[self getDataToSend] retain]; CountDownLatch *countDownLatch = [[CountDownLatch alloc] initCount:1]; [self.logger d:@"writeToCharacteristic: device=%@: going to write payload=%@", [self.logger SensitiveNSObject:[self getIdentifier]], [self.logger SensitiveNSObject:[toSend base64EncodedStringWithOptions:0]]]; [self.writeQ add:^{ if (self.peripheral == nil || self.peripheral.state != CBPeripheralStateConnected) { [self.logger e:@"writeToCharacteristic error: device=%@ not connected", [self.logger SensitiveNSObject:[self getIdentifier]]]; success = FALSE; [countDownLatch countDown]; return ; } [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]]]; [self.peripheral writeValue:toSend forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse]; } withCallback:^(NSError *error){ [self.logger d:@"writeToCharacteristic: device=%@: callback called for payload=%@ status=%@", [self.logger SensitiveNSObject:[self getIdentifier]], [self.logger SensitiveNSObject:[toSend base64EncodedStringWithOptions:0]], error]; success = error == nil ? TRUE : FALSE; [countDownLatch countDown]; } withDelay:0]; [countDownLatch await]; [countDownLatch release]; [toSend release]; // don't write EOD is error occurred if (!success) { [self.logger e:@"writeToCharacteristic error: device=%@: cancellation of the following writes", [self.logger SensitiveNSObject:[self getIdentifier]]]; return FALSE; } } else { [self.logger e:@"writeToCharacteristic error: device=%@ not connected", [self.logger SensitiveNSObject:[self getIdentifier]]]; return FALSE; } } if (eod) { dispatch_semaphore_t sema = dispatch_semaphore_create(0); [self.logger d:@"writeToCharacteristic: device=%@ going to write EOD", [self.logger SensitiveNSObject:[self getIdentifier]]]; [self.writeQ add:^{ [self.logger d:@"writeToCharacteristic: device=%@ writing EOD", [self.logger SensitiveNSObject:[self getIdentifier]]]; [self.peripheral writeValue:[@"EOD" dataUsingEncoding:NSUTF8StringEncoding] forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse]; } withCallback:^(NSError *error){ [self.logger d:@"writeToCharacteristic: device=%@: callback called for EOD", [self.logger SensitiveNSObject:[self getIdentifier]]]; success = error == nil ? 1 : 0; dispatch_semaphore_signal(sema); } withDelay:0]; dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); dispatch_release(sema); } return success; } } - (BOOL)readToCharacteristic:(CBCharacteristic *) characteristic { [self.logger d:@"readToCharacteristic called: device=%@", [self.logger SensitiveNSObject:[self getIdentifier]]]; if (self.peripheral == nil || self.peripheral.state != CBPeripheralStateConnected) { [self.logger e:@"readToCharacteristic error: device=%@ is not connected", [self.logger SensitiveNSObject:[self getIdentifier]]]; return FALSE; } __block BOOL success = FALSE; CountDownLatch *countDownLatch = [[CountDownLatch alloc] initCount:1]; [self.writeQ add:^{ [self.logger d:@"readToCharacteristic: device=%@: in queue", [self.logger SensitiveNSObject:[self getIdentifier]]]; if (self.peripheral == nil || self.peripheral.state != CBPeripheralStateConnected) { [self.logger e:@"readToCharacteristic: device=%@ is not connected", [self.logger SensitiveNSObject:[self getIdentifier]]]; success = FALSE; [countDownLatch countDown]; return ; } [self.peripheral readValueForCharacteristic:characteristic]; } withCallback:^(NSError *error){ if (error == nil) { [self.logger d:@"readToCharacteristic: device=%@: callback called with success", [self.logger SensitiveNSObject:[self getIdentifier]]]; success = TRUE; } else { [self.logger e:@"readToCharacteristic error: device=%@ error=%@ in callback", [self.logger SensitiveNSObject:[self getIdentifier]], error]; success = FALSE; } [countDownLatch countDown]; } withDelay:0]; [countDownLatch await]; [countDownLatch release]; return success; } - (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error { [self.writeQ completedTask:error]; if (error) { [self.logger e:@"didUpdateNotificationStateForCharacteristic error: device=%@ characteristic=%@ error=%@", [self.logger SensitiveNSObject:[self getIdentifier]], [characteristic.UUID UUIDString], error]; [self.manager disconnect:self]; return; } self.peer = [self.manager.peerManager registerDevice:self withPeerID:self.remotePeerID isClient:TRUE]; if (self.peer == nil) { [self.logger e:@"didUpdateNotificationStateForCharacteristic error: device=%@: registerDevice failed", [self.logger SensitiveNSObject:[self getIdentifier]]]; [self.manager disconnect:self]; } else { [self.logger d:@"didUpdateNotificationStateForCharacteristic: device=%@: registerDevice successed", [self.logger SensitiveNSObject:[self getIdentifier]]]; } } // Called when the value of the characteristic changed, whether by readValueForCharacteristic: or by a notification after a subscription - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error { [self.logger d:@"didUpdateValueForCharacteristic called: device=%@ characteristic=%@", [self.logger SensitiveNSObject:[self getIdentifier]], [characteristic.UUID UUIDString]]; if (error) { [self.logger e:@"didUpdateValueForCharacteristic error: device=%@ error=%@", [self.logger SensitiveNSObject:[self getIdentifier]], error]; [self.manager disconnect:self]; [self.writeQ completedTask:error]; return; } if ([characteristic.UUID isEqual:self.manager.peerUUID]) { if (characteristic.value != nil) { int psm; [[characteristic.value subdataWithRange:NSMakeRange(0, 4)] getBytes:&psm length:sizeof(psm)]; self.psm = NSSwapBigIntToHost(psm); NSString* remotePeerID = [NSString stringWithUTF8String: [[characteristic.value subdataWithRange:NSMakeRange(4, characteristic.value.length - 4)] bytes]]; [self.logger d:@"didUpdateValueForCharacteristic: device=%@ PSM=%d remotePID=%@", [self.logger SensitiveNSObject:[self getIdentifier]], self.psm, [self.logger SensitiveNSObject:remotePeerID]]; self.remotePeerID = remotePeerID; [self.writeQ completedTask:nil]; } else { [self.logger e:@"didUpdateValueForCharacteristic error: device=%@: characteristic doesn't have any value", [self.logger SensitiveNSObject:[self getIdentifier]]]; [self.writeQ completedTask:[NSError errorWithDomain:@LOCAL_DOMAIN code:200 userInfo:@{@"Error reason": @"Empty value"}]]; } } else if ([characteristic.UUID isEqual:self.manager.writerUUID]) { [self handleIncomingData:characteristic.value]; } else { [self.logger e:@"didUpdateValueForCharacteristic error: device=%@: bad characteristic requested", [self.logger SensitiveNSObject:[self getIdentifier]]]; } } - (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error { [self.logger d:@"didWriteValueForCharacteristic called: device=%@", [self.logger SensitiveNSObject:[self getIdentifier]]]; if (error) { [self.logger e:@"didWriteValueForCharacteristic error: device=%@ characteristic=%@ error=%@", [self.logger SensitiveNSObject:[self getIdentifier]], [characteristic.UUID UUIDString], error]; } [self.writeQ completedTask:error]; } #pragma mark - Characteristic Discovery - (void)discoverCharacteristics:(nullable NSArray *)characteristics forService:(CBService *)service { if (self.peripheral == nil || self.peripheral.state != CBPeripheralStateConnected) { [self.logger e:@"discoverCharacteristics error: device=%@ is not connected", [self.logger SensitiveNSObject:[self getIdentifier]]]; [self.manager disconnect:self]; return ; } [self.connectionQ add:^{ [self.peripheral discoverCharacteristics:characteristics forService:service]; } withCallback:nil withDelay:0]; } - (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error { [self.logger d:@"didDiscoverCharacteristicsForService called: device=%@", [self.logger SensitiveNSObject:[self getIdentifier]]]; [self.connectionQ completedTask:error]; if (error) { [self.logger e:@"didDiscoverCharacteristicsForService error: device=%@ error=%@", [self.logger SensitiveNSObject:[self getIdentifier]], error]; [self.manager disconnect:self]; return; } for (CBCharacteristic *chr in service.characteristics) { if ([chr.UUID isEqual:self.manager.peerUUID]) { self.peerIDCharacteristic = chr; [self.logger d:@"didDiscoverCharacteristicsForService: device=%@: peerID characteristic found", [self.logger SensitiveNSObject:[self getIdentifier]]]; } else if ([chr.UUID isEqual:self.manager.writerUUID]) { self.writerCharacteristic = chr; [self.logger d:@"didDiscoverCharacteristicsForService: device=%@: writer characteristic found", [self.logger SensitiveNSObject:[self getIdentifier]]]; } } if (self.peerIDCharacteristic == nil || self.writerCharacteristic == nil) { [self.logger e:@"didDiscoverCharacteristicsForService error: device=%@: not all characteristics found", [self.logger SensitiveNSObject:[self getIdentifier]]]; [self.manager disconnect:self]; return ; } dispatch_async(dispatch_get_global_queue(0, 0), ^{ [self handshake]; }); } #pragma mark - Services Discovery - (void)discoverServices:(NSArray *)serviceUUIDs { if (self.peripheral == nil || self.peripheral.state != CBPeripheralStateConnected) { [self.logger e:@"discoverServices error: device=%@ is not connected", [self.logger SensitiveNSObject:[self getIdentifier]]]; [self.manager disconnect:self]; return ; } self.peripheral.delegate = self; [self.connectionQ add:^{ [self.peripheral discoverServices:serviceUUIDs]; } withCallback:nil withDelay:0]; } - (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error { [self.logger d:@"didDiscoverServices called: device=%@", [self.logger SensitiveNSObject:[self getIdentifier]]]; [self.connectionQ completedTask:error]; if (error) { [self.logger e:@"didDiscoverServices error: device=%@ error=%@", [self.logger SensitiveNSObject:[self getIdentifier]], error]; [self.manager disconnect:self]; return; } CBService *service = getService(self.peripheral.services, [self.manager.serviceUUID UUIDString]); if (service == nil) { [self.logger e:@"didDiscoverServices error: device=%@: service not found", [self.logger SensitiveNSObject:[self getIdentifier]]]; [self.manager disconnect:self]; return; } [self discoverCharacteristics:@[self.manager.peerUUID, self.manager.writerUUID,] forService:service]; } #pragma mark - L2cap - (BOOL) negotiateL2cap { [self.logger d:@"negotiateL2cap called: device=%@", [self.logger SensitiveNSObject:[self getIdentifier]]]; if (self.peripheral == nil || self.peripheral.state != CBPeripheralStateConnected) { [self.logger e:@"negotiateL2cap error: device=%@ is not connected", [self.logger SensitiveNSObject:[self getIdentifier]]]; return FALSE; } __block BOOL success = FALSE; CountDownLatch *countDownLatch = [[CountDownLatch alloc] initCount:1]; if (@available(iOS 11.0, *)) { if (self.psm != 0) { [self.connectionQ add:^{ [self.logger d:@"negotiateL2cap: device=%@: opening L2cap channel", [self.logger SensitiveNSObject:[self getIdentifier]]]; [self.peripheral openL2CAPChannel:self.psm]; } withCallback:^(NSError *error){ if (error == nil) { [self.logger d:@"negotiateL2cap: device=%@: callback called with success", [self.logger SensitiveNSObject:[self getIdentifier]]]; success = TRUE; } else { [self.logger e:@"negotiateL2cap error: device=%@ error=%@ in callback", [self.logger SensitiveNSObject:[self getIdentifier]], error]; success = FALSE; } [countDownLatch countDown]; } withDelay:0]; [countDownLatch await]; [countDownLatch release]; } else { [self.logger d:@"negotiateL2cap: device=%@: central peripheral doesn't support L2CAP, aborting negotiation", [self.logger SensitiveNSObject:[self getIdentifier]]]; success = TRUE; // return TRUE to continue connection without L2cap } } else { [self.logger d:@"negotiateL2cap: device=%@: iOS 11+ is required", [self.logger SensitiveNSObject:[self getIdentifier]]]; success = TRUE; // return TRUE to continue connection without L2cap } return success; } - (BOOL)l2capWrite:(NSData *__nonnull)data { __block BOOL success = FALSE; if (self.l2capChannel != nil) { dispatch_semaphore_t sema = dispatch_semaphore_create(0); [self.writeQ add:^{ @synchronized (self.writerLatch) { [self.logger d:@"l2capWrite: device=%@ len=%lu base64=%@", [self.logger SensitiveNSObject:[self getIdentifier]], [data length], [self.logger SensitiveNSObject:[data base64EncodedStringWithOptions:0]]]; if ([self.logger showSensitiveData]) { [BleManager printLongLog:[BleManager NSDataToHex:data]]; } self.l2capWriteIndex = 0; self.l2capWriteData = data; if ([self.l2capChannel.outputStream hasSpaceAvailable]) { uint8_t *readBytes = (uint8_t *)[self.l2capWriteData bytes]; NSUInteger data_len = [data length]; NSUInteger len = (data_len >= L2CAP_BUFFER) ? L2CAP_BUFFER : (data_len); uint8_t buf[len]; (void)memcpy(buf, readBytes, len); self.l2capWriteIndex = [self.l2capChannel.outputStream write:(const uint8_t *)buf maxLength:len]; [self.logger d:@"l2capWrite: device=%@: wrote len=%zd", [self.logger SensitiveNSObject:[self getIdentifier]], self.l2capWriteIndex]; if (self.l2capWriteIndex == -1) { [self.logger e:@"l2capWrite error: device=%@", [self.logger SensitiveNSObject:[self getIdentifier]], self.l2capWriteIndex]; self.l2capWriteData = nil; [self.writeQ completedTask:[NSError errorWithDomain:@LOCAL_DOMAIN code:200 userInfo:@{@"Error reason": @"write error"}]]; return ; } if (self.l2capWriteIndex < data_len) { // write next data chunk when callback stream handleEvent: NSStreamEventHasSpaceAvailable is called [self.logger d:@"l2capWrite: device=%@: write completed but need more write space to send all data, waiting...", [self.logger SensitiveNSObject:[self getIdentifier]], self.l2capWriteIndex]; } else { [self.logger d:@"l2capWrite: device=%@: write completed and all data send", [self.logger SensitiveNSObject:[self getIdentifier]]]; self.l2capWriteData = nil; [self.writeQ completedTask:nil]; } } else { [self.logger d:@"l2capWrite: device=%@: need some space available, waiting...", [self.logger SensitiveNSObject:[self getIdentifier]]]; } } } withCallback:^(NSError *error) { if (error == nil) { [self.logger d:@"l2capWrite: device=%@: callback called with success", [self.logger SensitiveNSObject:[self getIdentifier]]]; success = TRUE; } else { [self.logger e:@"l2capWrite error: device=%@ error=%@ in callback", [self.logger SensitiveNSObject:[self getIdentifier]], error]; success = FALSE; } dispatch_semaphore_signal(sema); } withDelay:0]; dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); dispatch_release(sema); return success; } else { [self.logger e:@"l2capWrite error: device=%@: channel not set", [self.logger SensitiveNSObject:[self getIdentifier]]]; return FALSE; } } - (void)peripheral:(CBPeripheral *)peripheral didOpenL2CAPChannel:(CBL2CAPChannel *)channel error:(NSError *)error API_AVAILABLE(ios(11.0)) { [self.logger d:@"didOpenL2CAPChannel called: device=%@", [self.logger SensitiveNSObject:[self getIdentifier]]]; if (error != nil) { [self.logger e:@"didOpenL2CAPChannel Error: device=%@ error=%@", [self.logger SensitiveNSObject:[self getIdentifier]], error]; [self.connectionQ completedTask:error]; return ; } self.l2capChannel = channel; self.l2capThread = [[NSThread alloc] initWithTarget:self selector:@selector(setupL2capStreams) object:nil]; [self.l2capThread start]; self.l2capClientHandshakeRunning = TRUE; self.useL2cap = [self testL2cap]; self.l2capClientHandshakeRunning = FALSE; // wait that server complete L2CAP tests [NSThread sleepForTimeInterval:2.0f]; [self.connectionQ completedTask:nil]; } - (void)setupL2capStreams { [self.logger d:@"setupL2capStreams called: device=%@", [self.logger SensitiveNSObject:[self getIdentifier]]]; self.l2capChannel.inputStream.delegate = self; [self.l2capChannel.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; [self.l2capChannel.inputStream open]; self.l2capChannel.outputStream.delegate = self; [self.l2capChannel.outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; [self.l2capChannel.outputStream open]; @autoreleasepool { do { [[NSRunLoop currentRunLoop] run]; } while (self.peer != nil && [self.peer isConnected]); } } - (NSMutableData *__nonnull)createRandomNSData:(int) capacity { NSMutableData* theData = [NSMutableData dataWithCapacity:capacity]; for (unsigned int i = 0 ; i < capacity / 4 ; ++i ) { u_int32_t randomBits = arc4random(); [theData appendBytes:(void *)&randomBits length:4]; } return theData; } // Test contains 2 steps: // 1) client sends local PID and waits for receiving remote PID // 2) client sends remote PID in response of 1) to the server - (BOOL)testL2cap { self.l2capHandshakeStepStatus = FALSE; self.l2capHandshakeRecvData = [NSMutableData dataWithCapacity:L2CAP_HANDSHAKE_DATA]; self.l2capHandshakeLatch = [[CountDownLatch alloc] initCount:1]; self.l2capHandshakeBlock = dispatch_block_create(DISPATCH_BLOCK_INHERIT_QOS_CLASS, ^{ [self.logger e:@"testL2cap: device=%@: timeout hired", [self.logger SensitiveNSObject:[self getIdentifier]]]; [self.l2capHandshakeLatch countDown]; }); dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC), dispatch_get_main_queue(), self.l2capHandshakeBlock); // step 1 [self.logger d:@"testL2cap: device=%@: client going to write the 1st payload", [self.logger SensitiveNSObject:[self getIdentifier]]]; self.l2capHandshakeData = [self createRandomNSData:L2CAP_HANDSHAKE_DATA]; if (![self l2capWrite:self.l2capHandshakeData]) { [self.logger e:@"testL2cap error: device=%@: client write error", [self.logger SensitiveNSObject:[self getIdentifier]]]; dispatch_block_cancel(self.l2capHandshakeBlock); self.l2capHandshakeData = nil; self.l2capHandshakeRecvData = nil; return FALSE; } // waiting for receiving remote PID [self.l2capHandshakeLatch await]; self.l2capHandshakeData = nil; self.l2capHandshakeRecvData = nil; // step 2 if (self.l2capHandshakeStepStatus) { [self.logger d:@"testL2cap: device=%@: client going to write the 2nd payload", [self.logger SensitiveNSObject:[self getIdentifier]]]; if (![self l2capWrite:[self.remotePeerID dataUsingEncoding:NSUTF8StringEncoding]]) { [self.logger e:@"testL2cap error: device=%@: client write error", [self.logger SensitiveNSObject:[self getIdentifier]]]; return FALSE; } [self.logger d:@"testL2cap: device=%@: client handshake completed", [self.logger SensitiveNSObject:[self getIdentifier]]]; return TRUE; } return FALSE; } - (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode { switch(eventCode) { case NSStreamEventNone: { [self.logger d:@"stream handleEvent: NSStreamEventNone: device=%@", [self.logger SensitiveNSObject:[self getIdentifier]]]; break; } case NSStreamEventOpenCompleted: { [self.logger d:@"stream handleEvent: NSStreamEventOpenCompleted: device=%@", [self.logger SensitiveNSObject:[self getIdentifier]]]; break; } case NSStreamEventHasBytesAvailable: { [self.logger d:@"stream handleEvent: NSStreamEventHasBytesAvailable: device=%@", [self.logger SensitiveNSObject:[self getIdentifier]]]; uint8_t buf[L2CAP_BUFFER]; NSInteger len = 0; len = [(NSInputStream *)stream read:buf maxLength:L2CAP_BUFFER]; if(len > 0) { NSData *received = [NSData dataWithBytes:buf length:len]; [self.logger d:@"stream handleEvent: NSStreamEventHasBytesAvailable: device=%@ read length=%lu value=%@", [self.logger SensitiveNSObject:[self getIdentifier]], len, [self.logger SensitiveNSObject:received]]; [self handleIncomingData:received]; } else { [self.logger e:@"stream handleEvent error: NSStreamEventHasBytesAvailable: device=%@: nothing to read", [self.logger SensitiveNSObject:[self getIdentifier]]]; } break; } case NSStreamEventHasSpaceAvailable: { [self.logger d:@"stream handleEvent: NSStreamEventHasSpaceAvailable: device=%@", [self.logger SensitiveNSObject:[self getIdentifier]]]; if ((self.peer != nil && [self.peer isConnected]) || self.l2capServerHandshakeRunning || self.l2capClientHandshakeRunning) { @synchronized (self.writerLatch) { if (self.l2capWriteData != nil) { uint8_t *readBytes = (uint8_t *)[self.l2capWriteData bytes]; readBytes += self.l2capWriteIndex; NSUInteger data_len = [self.l2capWriteData length]; NSUInteger len = ((data_len - self.l2capWriteIndex >= L2CAP_BUFFER) ? L2CAP_BUFFER : (data_len - self.l2capWriteIndex)); uint8_t buf[len]; (void)memcpy(buf, readBytes, len); if ([self.logger showSensitiveData]) { [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]]]; } NSInteger wroteLen = [(NSOutputStream *)stream write:(const uint8_t *)buf maxLength:len]; [self.logger d:@"stream handleEvent: NSStreamEventHasSpaceAvailable: device=%@ wrote data offset=%lu len=%zd", [self.logger SensitiveNSObject:[self getIdentifier]], self.l2capWriteIndex, wroteLen]; if (wroteLen == -1) { [self.logger e:@"stream handleEvent error: NSStreamEventHasSpaceAvailable: device=%@ write: error", [self.logger SensitiveNSObject:[self getIdentifier]]]; self.l2capWriteData = nil; [self.writeQ completedTask:[NSError errorWithDomain:@LOCAL_DOMAIN code:200 userInfo:@{@"Error reason": @"write error"}]]; break; } self.l2capWriteIndex += wroteLen; if ([self.l2capWriteData length] == self.l2capWriteIndex) { [self.logger d:@"stream handleEvent: NSStreamEventHasSpaceAvailable: device=%@: write completed", [self.logger SensitiveNSObject:[self getIdentifier]]]; self.l2capWriteData = nil; [self.writeQ completedTask:nil]; } } else { [self.logger d:@"stream handleEvent: NSStreamEventHasSpaceAvailable: device=%@: no data to write", [self.logger SensitiveNSObject:[self getIdentifier]]]; } } } else { [self.logger e:@"stream handleEvent error: NSStreamEventHasSpaceAvailable: device=%@: device is not connected", [self.logger SensitiveNSObject:[self getIdentifier]]]; } break; } case NSStreamEventErrorOccurred: { [self.logger d:@"stream handleEvent: NSStreamEventErrorOccurred: device=%@", [self.logger SensitiveNSObject:[self getIdentifier]]]; [self.manager disconnect:self]; break; } case NSStreamEventEndEncountered: { // (d4ryl00): not sure how to handle this case [self.logger d:@"stream handleEvent: NSStreamEventEndEncountered: device=%@", [self.logger SensitiveNSObject:[self getIdentifier]]]; if (self.l2capChannel.outputStream == stream) { NSData *newData = [stream propertyForKey:NSStreamDataWrittenToMemoryStreamKey]; if (!newData) { [self.logger d:@"stream handleEvent: NSStreamEventEndEncountered: device=%@: no more data", [self.logger SensitiveNSObject:[self getIdentifier]]]; } else { [self.logger d:@"stream handleEvent: NSStreamEventEndEncountered: device=%@: data to process", [self.logger SensitiveNSObject:[self getIdentifier]]]; [self handleIncomingData:newData]; } } stream.delegate = nil; [stream close]; [stream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; if (self.l2capThread != nil) { [self.l2capThread cancel]; [self.l2capThread release]; self.l2capThread = nil; } [self.manager disconnect:self]; break; } } } @end ================================================ FILE: pkg/ble-driver/BleInterface_darwin.h ================================================ // // BleInterface.h // ble // // Created by sacha on 26/09/2018. // Copyright © 2018 sacha. All rights reserved. // #import #import #import #import #import "BleManager_darwin.h" #import "Logger.h" #ifndef BleInterface_h #define BleInterface_h void BLEStart(char *localPID); void BLEStop(void); int BLESendToPeer(char *remotePID, void *payload, int length); int BLEDialPeer(char *remotePID); void BLECloseConnWithPeer(char *remotePID); int BLEBridgeHandleFoundPeer(NSString *remotePID); void BLEBridgeHandleLostPeer(NSString *remotePID); void BLEBridgeReceiveFromPeer(NSString *remotePID, NSData *payload); void BLEBridgeLog(enum level level, NSString *message); void BLEUseExternalLogger(void); #endif /* BleInterface_h */ ================================================ FILE: pkg/ble-driver/BleInterface_darwin.m ================================================ // +build darwin,!noproximitytransport // // BleInterface.m // ble // // Created by sacha on 26/09/2018. // Copyright © 2018 sacha. All rights reserved. // #import "BleInterface_darwin.h" // This functions are Go functions so they aren't defined here extern int BLEHandleFoundPeer(char *); extern void BLEHandleLostPeer(char *); extern void BLEReceiveFromPeer(char *, void *, unsigned long); extern void BLELog(enum level level, const char *message); static BleManager *manager = nil; BOOL gBLEUseExternalLogger = FALSE; void handleException(NSException* exception) { NSLog(@"Unhandled exception %@", exception); } BleManager* getManager(void) { @synchronized([BleManager class]) { if(!manager) { NSLog(@"BleManager: initialization"); manager = [[BleManager alloc] initDriver:gBLEUseExternalLogger]; } } return manager; } void releaseManager(void) { @synchronized([BleManager class]) { if(manager) { NSLog(@"releaseManager"); [manager release]; manager = nil; } } } #pragma mark - incoming API functions void BLEStart(char *localPID) { NSLog(@"BLEStart called"); @autoreleasepool { NSString *localPIDString = [NSString stringWithUTF8String:localPID]; [getManager() setLocalPID:localPIDString]; [getManager().logger i:@"BLEStart: pid=%@", [getManager().logger SensitiveNSObject:localPIDString]]; [getManager() setID:[localPIDString substringWithRange:NSMakeRange([localPIDString length] - 4, 4)]]; [getManager() startScanning]; [getManager() startAdvertising]; NSSetUncaughtExceptionHandler(handleException); } } // TODO: Implement this, check if error void BLEStop(void) { [getManager().logger i:@"BLEStop"]; [getManager() stopScanning]; [getManager() stopAdvertising]; [getManager() closeAllConnections]; releaseManager(); } int BLESendToPeer(char *remotePID, void *payload, int length) { int status = 0; NSString *cPID = [[NSString alloc] initWithUTF8String:remotePID]; NSData *cPayload = [[NSData alloc] initWithBytes:payload length:length]; BertyDevice *bDevice = [getManager() findPeripheralFromPID:cPID]; if (bDevice == nil) { [getManager().logger e:@"BLESendToPeer error: no device found"]; return 0; } if (bDevice.peer == nil) { [getManager().logger e:@"BLESendToPeer error: peer object not found"]; return 0; } if (bDevice.useL2cap && bDevice.l2capChannel != nil) { status = [bDevice l2capWrite:cPayload]; } else { if ([bDevice.peer isClientReady]) { status = [bDevice writeToCharacteristic:cPayload forCharacteristic:bDevice.writerCharacteristic withEOD:FALSE]; } else if ([bDevice.peer isServerReady]) { status = [getManager() writeAndNotify:bDevice data:cPayload]; } else { [getManager().logger e:@"BLESendToPeer error: device not connected"]; } } [cPID release]; [cPayload release]; return status; } int BLEDialPeer(char *remotePID) { BertyDevice *bDevice = [getManager() findPeripheralFromPID:[NSString stringWithUTF8String:remotePID]]; if (bDevice != nil) { return 1; } return 0; } // TODO: Implement this void BLECloseConnWithPeer(char *remotePID) { [getManager().logger i:@"BLECloseConnWithPeer called: remotePID=%@", [getManager().logger SensitiveString:remotePID]]; BertyDevice *bDevice = [getManager() findPeripheralFromPID:[NSString stringWithUTF8String:remotePID]]; if (bDevice != nil) { [getManager() disconnect:bDevice]; } } // Use BLEBridgeLog to write logs to the external logger void BLEUseExternalLogger(void) { gBLEUseExternalLogger = TRUE; } #pragma mark - outgoing API functions int BLEBridgeHandleFoundPeer(NSString *remotePID) { char *cPID = (char *)[remotePID UTF8String]; if (BLEHandleFoundPeer(cPID)) { return (1); } return (0); } void BLEBridgeHandleLostPeer(NSString *remotePID) { char *cPID = (char *)[remotePID UTF8String]; BLEHandleLostPeer(cPID); } void BLEBridgeReceiveFromPeer(NSString *remotePID, NSData *payload) { char *cPID = (char *)[remotePID UTF8String]; char *cPayload = (char *)[payload bytes]; int length = (int)[payload length]; BLEReceiveFromPeer(cPID, cPayload, length); } // Write logs to the external logger void BLEBridgeLog(enum level level, NSString *message) { char *cMessage = (char *)[message UTF8String]; BLELog(level, cMessage); } ================================================ FILE: pkg/ble-driver/BleManager_darwin.h ================================================ // // BleManager.h // ble // // Created by sacha on 23/05/2019. // Copyright © 2019 berty. All rights reserved. // #import #import #import "BleInterface_darwin.h" #import "BertyDevice_darwin.h" #import "PeerManager.h" #import "CountDownLatch_darwin.h" #import "WriteDataCache.h" #define LOCAL_DOMAIN "tech.berty.bty" NS_ASSUME_NONNULL_BEGIN @interface BleManager : NSObject + (CBUUID *__nonnull)serviceUUID; + (CBUUID *__nonnull)peerUUID; + (CBUUID *__nonnull)writerUUID; + (NSString *__nonnull)NSDataToHex:(NSData *__nonnull)data; + (void) printLongLog:(NSString *__nonnull)message; @property (nonatomic, strong, nullable) Logger *logger; @property (nonatomic, strong, nonnull) PeerManager *peerManager; @property (readwrite) BOOL pmEnable; @property (readwrite) BOOL cmEnable; @property (readwrite) int psm; @property (nonatomic, strong, nonnull) CBMutableService *bertyService; @property (nonatomic, strong, nonnull) CBMutableService *nameService; @property (nonatomic, strong, nonnull) CBMutableCharacteristic *peerIDCharacteristic; @property (nonatomic, strong, nonnull) CBMutableCharacteristic *writerCharacteristic; @property (nonatomic, strong, nullable) NSString *localPID; @property (nonatomic, strong, nonnull) NSString *ID; @property (nonatomic, strong, nonnull) CBUUID *serviceUUID; @property (nonatomic, strong, nonnull) CBUUID *peerUUID; @property (nonatomic, strong, nonnull) CBUUID *writerUUID; @property (nonatomic, strong, nonnull) NSMutableArray *bDevices; @property (nonatomic, strong, nonnull) CBCentralManager* cManager; @property (nonatomic, strong, nonnull) CBPeripheralManager* pManager; @property (nonatomic, strong, nonnull) CountDownLatch *bleOn; @property (nonatomic, strong, nonnull) CountDownLatch *serviceAdded; @property (nonatomic, strong, nullable) NSTimer *scannerTimer; @property (nonatomic, readwrite, getter=isScanning) BOOL scanning; @property (nonatomic, strong, nullable) WriteDataCache *writeCache; @property (nonatomic, strong, nullable) CountDownLatch *writerLactch; @property (readwrite) BOOL writeStatus; - (instancetype __nonnull) initDriver:(BOOL)useExternalLogger; - (void)addService; - (void)startScanning; - (void)toggleScanner:(NSTimer *__nonnull)timer; - (void)stopScanning; - (void)startAdvertising; - (void)stopAdvertising; - (void)disconnect:(BertyDevice *__nonnull)device; - (void)closeAllConnections; - (BertyDevice *__nullable)findPeripheralFromIdentifier:(NSUUID *__nonnull)identifier; - (BertyDevice *__nullable)findPeripheralFromPID:(NSString *__nonnull)peerID; - (BOOL)writeAndNotify:(BertyDevice *__nonnull)device data:(NSData *__nonnull)data; @end NS_ASSUME_NONNULL_END ================================================ FILE: pkg/ble-driver/BleManager_darwin.m ================================================ // +build darwin,!noproximitytransport // // BleManager.m // ble // // Created by sacha on 23/05/2019. // Copyright © 2019 berty. All rights reserved. // #import "BleManager_darwin.h" @implementation BleManager static NSString* const __nonnull SERVICE_UUID = @"00004240-0000-1000-8000-00805F9B34FB"; static NSString* const __nonnull WRITER_UUID = @"00004242-0000-1000-8000-00805F9B34FB"; static NSString* const __nonnull PEER_ID_UUID = @"00004241-0000-1000-8000-00805F9B34FB"; + (CBUUID *)serviceUUID { return [CBUUID UUIDWithString:SERVICE_UUID]; } + (CBUUID *)peerUUID { return [CBUUID UUIDWithString:PEER_ID_UUID]; } + (CBUUID *)writerUUID { return [CBUUID UUIDWithString:WRITER_UUID]; } static inline char itoh(int i) { if (i > 9) return 'A' + (i - 10); return '0' + i; } + (NSString *__nonnull)NSDataToHex:(NSData *__nonnull)data { NSUInteger i, len; unsigned char *buf, *bytes; len = data.length; bytes = (unsigned char*)data.bytes; buf = malloc(len*2); for (i=0; i> 4) & 0xF); buf[i*2+1] = itoh(bytes[i] & 0xF); } return [[[NSString alloc] initWithBytesNoCopy:buf length:len*2 encoding:NSASCIIStringEncoding freeWhenDone:YES] autorelease]; } + (void) printLongLog:(NSString *__nonnull)message { if ([message length] > 4000) { NSLog(@"message.length=%lu", [message length]); unsigned long int chunkCount = [message length] / 4000; // integer division for (int i = 0; i <= chunkCount; i++) { int max = 4000 * (i + 1); if (max >= [message length]) { NSLog(@"chunk %d of %lu: %@", i, chunkCount, [message substringWithRange:NSMakeRange(4000 * i, [message length] - (4000 * i))]); } else { NSLog(@"chunk %d of %lu: %@", i, chunkCount, [message substringWithRange:NSMakeRange(4000 * i, 4000)]); } } } else { NSLog(@"%@", message); } } // TODO: No need to check error on this? - (instancetype __nonnull) initDriver:(BOOL)useExternalLogger { self = [super init]; if (self) { BOOL showSensitiveData = FALSE; if (useExternalLogger) { _logger = [[Logger alloc] initWithExternalLoggerAndShowSensitiveData:showSensitiveData]; } else { _logger = [[Logger alloc] initLocalLoggerWithSubSystem:LOCAL_DOMAIN andCategorie:"BLE" showSensitiveData:showSensitiveData]; } _peerManager = [[PeerManager alloc] initWithLogger:_logger]; _cmEnable = FALSE; _pmEnable = FALSE; _scannerTimer = nil; _bleOn = [[CountDownLatch alloc] initCount:2]; _serviceAdded = [[CountDownLatch alloc] initCount:1]; _bDevices = [[NSMutableArray alloc] init]; _cManager = [[CBCentralManager alloc] initWithDelegate:self queue:dispatch_queue_create("CentralManager", DISPATCH_QUEUE_SERIAL) options:@{CBCentralManagerOptionShowPowerAlertKey:[NSNumber numberWithBool:NO]}]; _pManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:dispatch_queue_create("PeripheralManager", DISPATCH_QUEUE_SERIAL) options:@{CBPeripheralManagerOptionShowPowerAlertKey:[NSNumber numberWithBool:NO]}]; [self initService]; [self addService]; } return self; } - (void)initService { [self.logger d:@"initService called"]; _scanning = FALSE; _serviceUUID = [[CBUUID UUIDWithString:SERVICE_UUID] retain]; _peerUUID = [[CBUUID UUIDWithString:PEER_ID_UUID] retain]; _writerUUID = [[CBUUID UUIDWithString:WRITER_UUID] retain]; _peerIDCharacteristic = [[CBMutableCharacteristic alloc] initWithType:self.peerUUID properties:CBCharacteristicPropertyRead | CBCharacteristicPropertyWrite value:nil permissions:CBAttributePermissionsReadable | CBAttributePermissionsWriteable]; _writerCharacteristic = [[CBMutableCharacteristic alloc] initWithType:self.writerUUID properties:CBCharacteristicPropertyWrite | CBCharacteristicPropertyNotify value:nil permissions:CBAttributePermissionsWriteable]; _bertyService = [[CBMutableService alloc] initWithType:self.serviceUUID primary:YES]; _bertyService.characteristics = [@[self.writerCharacteristic, self.peerIDCharacteristic] retain]; } - (void)dealloc { [_logger release]; [_peerManager release]; [_bleOn release]; [_serviceAdded release]; [_bDevices release]; [_cManager release]; [_pManager release]; [_serviceUUID release]; [_peerUUID release]; [_writerUUID release]; [_peerIDCharacteristic release]; [_writerCharacteristic release]; [_bertyService.characteristics release]; [_bertyService release]; [super dealloc]; } - (void)addService { [self.logger d:@"addService: service=%@", [self.serviceUUID UUIDString]]; [self.bleOn await:5 withCancelBlock:^{ [self.logger e:@"addService error: timeout"]; }]; if (self.cmEnable && self.pmEnable) { [self.pManager addService:self.bertyService]; [self.serviceAdded await]; } } - (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(nullable NSError *)error { if (error) { [self.logger e:@"didAddService() error=%@", [error localizedFailureReason]]; } [self.logger d:@"didAddService: service=%@", [service.UUID UUIDString]]; [self.serviceAdded countDown]; } #pragma mark - go called functions - (void)startScanning { @synchronized (self.cManager) { if (self.cmEnable && !self.scanning) { if (self.localPID != nil) { [self.logger d:@"startScanning called"]; NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], CBCentralManagerScanOptionAllowDuplicatesKey, nil]; [self.cManager scanForPeripheralsWithServices:@[self.serviceUUID] options:options]; self.scanning = TRUE; dispatch_async(dispatch_get_main_queue(), ^(void){ self.scannerTimer = [NSTimer scheduledTimerWithTimeInterval:12.0 target:self selector:@selector(toggleScanner:) userInfo:nil repeats:YES]; }); } else { [self.logger e:@"startScanning error: localPID is null"]; } } else { [self.logger i:@"startScanning: scanner is already enabled"]; } } } - (void)toggleScanner:(NSTimer*)timer { if ([self.cManager isScanning]) { [self.logger d:@"toggleScanner: disable scanner"]; [self.cManager stopScan]; } else { [self.logger d:@"toggleScanner: enable scanner"]; NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], CBCentralManagerScanOptionAllowDuplicatesKey, nil]; [self.cManager scanForPeripheralsWithServices:@[self.serviceUUID] options:options]; } } - (void)stopScanning { @synchronized (self.cManager) { if (self.cmEnable && self.scanning) { [self.logger d:@"stopScanning called"]; dispatch_async(dispatch_get_main_queue(), ^{ if (self.scannerTimer != nil) { if ([self.scannerTimer isValid]) { [self.scannerTimer invalidate]; } self.scannerTimer = nil; } if ([self.cManager isScanning]) { [self.cManager stopScan]; } self.scanning = FALSE; }); } } } - (void)startAdvertising { @synchronized (self.pManager) { if (self.pmEnable && ![self.pManager isAdvertising]) { if (self.ID != nil) { [self.logger d:@"startAdvertising called: ID=%@", [self.logger SensitiveNSObject:self.ID]]; // publish l2cap channel self.psm = 0; if (@available(iOS 11.0, *)) { [self.pManager publishL2CAPChannelWithEncryption:false]; } [self.pManager startAdvertising:@{ CBAdvertisementDataLocalNameKey:self.ID, CBAdvertisementDataServiceUUIDsKey:@[self.serviceUUID]}]; } else { [self.logger e:@"startAdvertising error: local ID is null"]; } } } } - (void)stopAdvertising { @synchronized (self.pManager) { [self.logger d:@"stopAdvertising called"]; if (self.pmEnable && [self.pManager isAdvertising]) { if (@available(iOS 11.0, *)) { if (self.psm != 0) { [self.pManager unpublishL2CAPChannel:self.psm]; } } [self.pManager stopAdvertising]; } else { [self.logger e:@"stopAdvertising error: advertising not started"]; } } } // Only the client side can disconnect - (void)disconnect:(BertyDevice *__nonnull)device { [self.logger d:@"closeAllConnections called: debice=%@", [self.logger SensitiveNSObject:[device clientSideIdentifier]]]; if (device.peripheral != nil && device.clientSideIdentifier != nil) { [self.logger d:@"disconnect: client device=%@", [self.logger SensitiveNSObject:[device clientSideIdentifier]]]; if (device.peripheral.state == CBPeripheralStateConnecting || device.peripheral.state == CBPeripheralStateConnected) { [self.cManager cancelPeripheralConnection:device.peripheral]; } else { [self.logger d:@"disconnect: client device=%@ not connected", [self.logger SensitiveNSObject:[device clientSideIdentifier]]]; return ; } } else { [device closeBertyDevice]; } } - (void)closeAllConnections { [self.logger i:@"closeAllConnections called"]; if (self.cmEnable) { @synchronized (self.bDevices) { for (BertyDevice *device in self.bDevices) { [self disconnect:device]; } } } } - (BOOL)writeAndNotify:(BertyDevice *__nonnull)device data:(NSData *__nonnull)data { [self.logger d:@"writeAndNotify: device=%@ base64=%@", [self.logger SensitiveNSObject:[device clientSideIdentifier]], [self.logger SensitiveNSObject:[data base64EncodedStringWithOptions:0]]]; if ([self.logger showSensitiveData]) { [BleManager printLongLog:[BleManager NSDataToHex:data]]; } BOOL success = FALSE; NSUInteger mtu = device.cbCentral.maximumUpdateValueLength; NSUInteger offset = 0; NSUInteger dataLen = [data length]; while (offset < dataLen) { if (![device.peer isServerReady]) { [self.logger e:@"writeAndNotify error: device=%@ server not connected", [self.logger SensitiveNSObject:[device clientSideIdentifier]]]; return FALSE; } self.writerLactch = [[CountDownLatch alloc] initCount:1]; NSUInteger toWriteLen = (dataLen - offset) < mtu ? (dataLen - offset) : mtu; NSData *toWrite = [[data subdataWithRange:NSMakeRange(offset, toWriteLen)] retain]; if ([self.logger showSensitiveData]) { [self.logger d:@"writeAndNotify: device=%@ mtu=%lu base64=%@ data=%@", [device getIdentifier], mtu, [toWrite base64EncodedStringWithOptions:0], [BleManager NSDataToHex:toWrite]]; } // Need to add data to the cache prior to write it because sometime peripheralManagerIsReadyToUpdateSubscribers is called before data is put to the cache @synchronized (self.writeCache) { self.writeCache = [[WriteDataCache alloc] initWithDevice:device withData:toWrite]; [self.logger d:@"writeAndNotify: device=%@: data put in cache successfully", [self.logger SensitiveNSObject:[device getIdentifier]]]; } success = [self.pManager updateValue:toWrite forCharacteristic:self.writerCharacteristic onSubscribedCentrals:@[device.cbCentral]]; if (success) { [self.writerLactch countDown]; } else { [self.logger d:@"writeAndNotify: device=%@: operation queue is full and will be processed by the peripheralManagerIsReadyToUpdateSubscribers callback", [self.logger SensitiveNSObject:[device getIdentifier]]]; } [self.writerLactch await]; // take write status from peripheralManagerIsReadyToUpdateSubscribers if (!success) { success = self.writeStatus; } [self.writeCache release]; self.writeCache = nil; [self.writerLactch release]; self.writerLactch = nil; [toWrite release]; // this time write failed, don't continue if (!success) { break ; } offset += toWriteLen; } [self.logger d:@"writeAndNotify: device=%@: success=%d", [self.logger SensitiveNSObject:[device getIdentifier]], success]; return success; } #pragma mark - BertyDevice dict helper - (BertyDevice *)findPeripheral:(CBPeripheral *)peripheral { BertyDevice *result = nil; @synchronized (self.bDevices) { for (BertyDevice *bDevice in self.bDevices) { if (bDevice.peripheral == peripheral) { result = bDevice; break; } } } return result; } - (BertyDevice *)findPeripheralFromName:(NSString *) name { @synchronized (self.bDevices) { for (BertyDevice *bDevice in self.bDevices) { if ([bDevice.name isEqualToString:name]) { return bDevice; } } } return nil; } - (BertyDevice *)findPeripheralFromPID:(NSString *)peerID { BertyDevice *result = nil; @synchronized (self.bDevices) { for (BertyDevice *bDevice in self.bDevices) { if ([bDevice.remotePeerID isEqualToString:peerID]) { result = bDevice; break; } } } return result; } - (BertyDevice *)findPeripheralFromIdentifier:(NSUUID *)identifier { BertyDevice *result = nil; NSString *id = [identifier UUIDString]; @synchronized (self.bDevices) { for (BertyDevice *bDevice in self.bDevices) { if ([bDevice.clientSideIdentifier isEqual:id]) { result = bDevice; break; } else if ([bDevice.serverSideIdentifier isEqual:id]) { result = bDevice; break; } } } return result; } #pragma mark - CentraManagerDelegate - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral { [self.logger i:@"didConnectPeripheral called: device=%@", [self.logger SensitiveNSObject:[peripheral.identifier UUIDString]]]; BertyDevice *bDevice = [self findPeripheral:peripheral]; if (bDevice == nil) { [self.logger e:@"didConnectPeripheral error: device=%@ not found", [self.logger SensitiveNSObject:[peripheral.identifier UUIDString]]]; [self.cManager cancelPeripheralConnection:peripheral]; return ; } [bDevice handleConnect:nil]; } - (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error { [self.logger i:@"didFailToConnectPeripheral called: device=%@", [self.logger SensitiveNSObject:[peripheral.identifier UUIDString]]]; BertyDevice *bDevice = [self findPeripheral:peripheral]; if (bDevice == nil) { [self.logger e:@"didFailToConnectPeripheral error: device=%@ not found", [self.logger SensitiveNSObject:[peripheral.identifier UUIDString]]]; return ; } [bDevice handleConnect:error]; } - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI { NSString *id = nil; if (advertisementData && [advertisementData.allKeys containsObject:CBAdvertisementDataLocalNameKey]) { // between 2 iOS id = [advertisementData valueForKeyPath:CBAdvertisementDataLocalNameKey]; } else if (advertisementData && [advertisementData.allKeys containsObject:CBAdvertisementDataServiceDataKey]) { // between Android / iOS NSDictionary *data = [advertisementData valueForKey:CBAdvertisementDataServiceDataKey]; if (data) { CBUUID *uuid = [CBUUID UUIDWithString:@"4240"]; id = [[NSString alloc] initWithData:[data objectForKey:uuid] encoding:NSUTF8StringEncoding]; [id autorelease]; } else { [self.logger d:@"didDiscoverPeripheral error: device=%@: CBAdvertisementDataServiceDataKey doesn't contains any data", [self.logger SensitiveNSObject:[peripheral.identifier UUIDString]]]; return ; } } else { // verbose // [self.logger e:@"didDiscoverPeripheral error: device=%@ has not advertisement name", [self.logger SensitiveNSObject:[peripheral.identifier UUIDString]]]; return ; } if ([id length] == 0) { [self.logger d:@"didDiscoverPeripheral error: device=%@: id is empty", [self.logger SensitiveNSObject:[peripheral.identifier UUIDString]]]; return ; } // only lower id can be client if ([self.ID compare:id] != NSOrderedAscending) { // Verbose // [self.logger d:@"didDiscoverPeripheral: device=%@: greater ID, cancel client connection", [self.logger SensitiveNSObject:[peripheral.identifier UUIDString]]]; return ; } BertyDevice *nDevice = [self findPeripheralFromIdentifier:peripheral.identifier]; if (nDevice != nil) { // peripheral already known if (nDevice.clientSideIdentifier != nil) { // peripheral already discovered return ; } // peripheral already known by CBPeripheralManager (advertising) // adding info given by CBCentralManager (scanning) [self.logger d:@"didDiscoverPeripheral: device=%@ id=%@: already known", [self.logger SensitiveNSObject:[peripheral.identifier UUIDString]], [self.logger SensitiveNSObject:id]]; nDevice.peripheral = peripheral; nDevice.clientSideIdentifier = [peripheral.identifier UUIDString]; } else { nDevice = [self findPeripheralFromName:id]; if (nDevice != nil && nDevice.peripheral != nil) { // device already known with another peripheral object return ; } // TODO: retest if bDevices is still null after @synchronized @synchronized (self.bDevices) { nDevice = [[BertyDevice alloc]initWithPeripheral:peripheral logger:self.logger central:self withName:id]; [self.bDevices addObject:nDevice]; [nDevice release]; [self.logger d:@"didDiscoverPeripheral: device=%@ added to BleManager.bDevices", [self.logger SensitiveNSObject:[peripheral.identifier UUIDString]]]; } } [self.logger d:@"didDiscoverPeripheral: device=%@ id=%@: found. Going to connect.", [self.logger SensitiveNSObject:[peripheral.identifier UUIDString]], [self.logger SensitiveNSObject:id]]; [nDevice connectWithOptions:nil]; } - (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error { [self.logger i:@"didDisconnectPeripheral called: device=%@ error=%@", [self.logger SensitiveNSObject:[peripheral.identifier UUIDString]], error]; BertyDevice *device = [self findPeripheral:peripheral]; if (device != nil) { [device closeBertyDevice]; @synchronized (self.bDevices) { [self.bDevices removeObject:device]; } } } #pragma mark - State Management - (void)peripheralManagerDidUpdateState:(nonnull CBPeripheralManager *)peripheral { NSString *stateString = nil; @synchronized (self.pManager) { self.pmEnable = FALSE; } switch(peripheral.state) { case CBManagerStateUnknown: { stateString = @"CBManagerStateUnknown"; break; } case CBManagerStateResetting: { stateString = @"CBManagerStateResetting"; break; } case CBManagerStateUnsupported: { stateString = @"CBManagerStateUnsupported"; break; } case CBManagerStateUnauthorized: { stateString = @"CBManagerStateUnauthorized"; break; } case CBManagerStatePoweredOff: { stateString = @"CBManagerStatePoweredOff"; break; } case CBManagerStatePoweredOn: { stateString = @"CBManagerStatePoweredOn"; @synchronized (self.pManager) { self.pmEnable = TRUE; } break; } default: { stateString = @"State unknown, update imminent."; break; } } [self.logger i:@"peripheralManagerDidUpdateState: %@", stateString]; [self.bleOn countDown]; } - (void)centralManagerDidUpdateState:(nonnull CBCentralManager *)central { NSString *stateString = nil; @synchronized (self.cManager) { self.cmEnable = FALSE; } switch(central.state) { case CBManagerStateResetting: { break; } case CBManagerStateUnsupported: { break; } case CBManagerStateUnauthorized: { break; } case CBManagerStatePoweredOff: { stateString = @"Bluetooth is currently powered off."; break; } case CBManagerStatePoweredOn: { stateString = @"Bluetooth is currently powered on and available to use."; @synchronized (self.cManager) { self.cmEnable = TRUE; } break; } default: { stateString = @"State unknown, update imminent."; break; } } [self.logger i:@"centralManagerDidUpdateState: %@", stateString]; [self.bleOn countDown]; } - (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic { [self.logger d:@"peripheralManager didSubscribeToCharacteristic called: device=%@", [self.logger SensitiveNSObject:[central.identifier UUIDString]]]; BertyDevice *device; if ((device = [self findPeripheralFromIdentifier:central.identifier]) == nil) { [self.logger e:@"peripheralManager didSubscribeToCharacteristic error: device=%@ not found", [self.logger SensitiveNSObject:[central.identifier UUIDString]]]; return ; } device.cbCentral = central; // Server doesn't know if the L2CAP handshake failed on the client side // so we have to set it manually at this step. device.l2capServerHandshakeRunning = FALSE; // complete handshake device.peer = [self.peerManager registerDevice:device withPeerID:device.remotePeerID isClient:FALSE]; if (device.peer == nil) { [self.logger e:@"peripheralManager didSubscribeToCharacteristic error: device=%@: registerDevice failed", [self.logger SensitiveNSObject:[central.identifier UUIDString]]]; return ; } else { [self.logger d:@"peripheralManager didSubscribeToCharacteristic: device=%@: registerDevice successed", [self.logger SensitiveNSObject:[central.identifier UUIDString]]]; } } // server disconnection callback entry point - (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic { [self.logger d:@"peripheralManager didUnsubscribeFromCharacteristic called: device=%@", [self.logger SensitiveNSObject:[central.identifier UUIDString]]]; BertyDevice *device; if ((device = [self findPeripheralFromIdentifier:central.identifier]) == nil) { [self.logger e:@"peripheralManager didUnsubscribeFromCharacteristic error: device=%@ not found", [self.logger SensitiveNSObject:[central.identifier UUIDString]]]; return ; } [device closeBertyDevice]; @synchronized (self.bDevices) { [self.bDevices removeObject:device]; } } - (void)peripheralManagerIsReadyToUpdateSubscribers:(CBPeripheralManager *)peripheral { [self.logger d:@"peripheralManager peripheralManagerIsReadyToUpdateSubscribers called"]; self.writeStatus = FALSE; @synchronized(self.writeCache) { if (self.writeCache != nil) { if (self.logger.showSensitiveData) { [self.logger d:@"peripheralManagerIsReadyToUpdateSubscribers: device=%@ base64=%@ data=%@", [self.writeCache.device getIdentifier], [self.writeCache.data base64EncodedStringWithOptions:0], [BleManager NSDataToHex:self.writeCache.data]]; } if (self.writerLactch == nil) { [self.logger e:@"peripheralManagerIsReadyToUpdateSubscribers error: writer latch is null"]; return ; } if (self.writeCache.device.peer == nil) { [self.logger e:@"peripheralManagerIsReadyToUpdateSubscribers error: peer object not found"]; [self.writerLactch countDown]; return ; } if (![self.writeCache.device.peer isServerReady]) { [self.logger e:@"peripheralManagerIsReadyToUpdateSubscribers error: server not connected"]; [self.writerLactch countDown]; return ; } self.writeStatus = [self.pManager updateValue:self.writeCache.data forCharacteristic:self.writerCharacteristic onSubscribedCentrals:@[self.writeCache.device.cbCentral]]; if (self.writeStatus) { [self.logger d:@"peripheralManagerIsReadyToUpdateSubscribers: device=%@: data sent", [self.logger SensitiveNSObject:[self.writeCache.device getIdentifier]]]; [self.writerLactch countDown]; } else { [self.logger d:@"peripheralManagerIsReadyToUpdateSubscribers: device=%@: operation queue is full, try later", [self.logger SensitiveNSObject:[self.writeCache.device getIdentifier]]]; return ; } } } } #pragma mark - read - (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request { [self.logger d:@"didReceiveReadRequests called: device=%@ offset=%lu", [self.logger SensitiveNSObject:[request.central.identifier UUIDString]], request.offset]; if ([request.characteristic.UUID isEqual:self.peerUUID]) { [self.logger d:@"didReceiveReadRequests: device=%@: use peerID characteristic", [self.logger SensitiveNSObject:[request.central.identifier UUIDString]]]; BertyDevice *device = [self findPeripheralFromIdentifier:request.central.identifier]; if (device == nil || device.remotePeerID == nil) { [self.logger e:@"didReceiveReadRequests: device=%@: need writeRequest completed before readRequest", [self.logger SensitiveNSObject:[request.central.identifier UUIDString]]]; [peripheral respondToRequest:request withResult:CBATTErrorReadNotPermitted]; return ; } // Write PSM to big endian int psm = NSSwapHostIntToBig(self.psm); NSMutableData *toSend = [[NSMutableData alloc] initWithBytes:&psm length:sizeof(psm)]; [toSend appendData:[self.localPID dataUsingEncoding:NSUTF8StringEncoding]]; request.value = toSend; [peripheral respondToRequest:request withResult:CBATTErrorSuccess]; [toSend release]; } else { [self.logger e:@"didReceiveReadRequests: device=%@: bad characteristic requested", [self.logger SensitiveNSObject:[request.central.identifier UUIDString]]]; [peripheral respondToRequest:request withResult:CBATTErrorRequestNotSupported]; return ; } } #pragma mark - write - (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray *)requests { BertyDevice *device; NSData *data = nil; for (CBATTRequest *request in requests) { [self.logger d:@"didReceiveWriteRequests: device=%@ base64=%@", [self.logger SensitiveNSObject:[request.central.identifier UUIDString]], [self.logger SensitiveNSObject:[data base64EncodedStringWithOptions:0]]]; if (self.logger.showSensitiveData) { [BleManager printLongLog:[BleManager NSDataToHex:data]]; } CBMutableCharacteristic *characteristic; @synchronized (self.bDevices) { // check if we hold a remote device of this type device = [self findPeripheralFromIdentifier:request.central.identifier]; if (device == nil) { device = [[BertyDevice alloc]initWithIdentifier:[request.central.identifier UUIDString] logger:self.logger central:self asClient:FALSE]; [self.bDevices addObject:device]; [device release]; [self.logger d:@"didReceiveWriteRequests: device=%@ added to BleManager.bDevices", [self.logger SensitiveNSObject:[request.central.identifier UUIDString]]]; } } if ([request.characteristic.UUID isEqual:self.writerUUID]) { characteristic = self.writerCharacteristic; } else if ([request.characteristic.UUID isEqual:self.peerUUID]) { characteristic = self.peerIDCharacteristic; } else { [self.logger e:@"didReceiveWriteRequests error: device=%@: bad characteristic requested", [self.logger SensitiveNSObject:[request.central.identifier UUIDString]]]; [device closeBertyDevice]; [peripheral respondToRequest:request withResult:CBATTErrorWriteNotPermitted]; return ; } data = request.value; BOOL(^handler)(NSData *) = [device.characteristicHandlers objectForKey:[request.characteristic.UUID UUIDString]]; if (!handler(data)) { [self.logger e:@"didReceiveWriteRequests error: device=%@: handle failed", [self.logger SensitiveNSObject:[request.central.identifier UUIDString]]]; [device closeBertyDevice]; [peripheral respondToRequest:request withResult:CBATTErrorWriteNotPermitted]; } // Process response back request.value = [self.localPID dataUsingEncoding:NSUTF8StringEncoding]; } [peripheral respondToRequest:[requests objectAtIndex:0] withResult:CBATTErrorSuccess]; } #pragma mark - L2cap - (void)peripheralManager:(CBPeripheralManager *)peripheral didPublishL2CAPChannel:(CBL2CAPPSM)PSM error:(NSError *)error { if (error != nil) { [self.logger e:@"peripheralManager didPublishL2CAPChannel error=%@", error]; return ; } [self.logger d:@"peripheralManager didPublishL2CAPChannel: PSM=%hu", PSM]; self.psm = PSM; } - (void)peripheralManager:(CBPeripheralManager *)peripheral didUnpublishL2CAPChannel:(CBL2CAPPSM)PSM error:(NSError *)error { [self.logger d:@"peripheralManager didUnpublishL2CAPChannel called"]; self.psm = 0; } - (void)peripheralManager:(CBPeripheralManager *)peripheral didOpenL2CAPChannel:(CBL2CAPChannel *)channel error:(NSError *)error API_AVAILABLE(ios(11.0)) { [self.logger d:@"peripheralManager didOpenL2CAPChannel called: device=%@", [self.logger SensitiveNSObject:[channel.peer.identifier UUIDString]]]; if (error != nil) { [self.logger e:@"peripheralManager didOpenL2CAPChannel error=%@", error]; return ; } BertyDevice *device; if ((device = [self findPeripheralFromIdentifier:channel.peer.identifier]) == nil) { [self.logger e:@"peripheralManager didOpenL2CAPChannel error: device=%@ not found", [self.logger SensitiveNSObject:[channel.peer.identifier UUIDString]]]; return ; } device.l2capChannel = channel; device.l2capThread = [[NSThread alloc] initWithBlock:^{ [self.logger d:@"peripheralManager didOpenL2CAPChannel: device=%@: in thread", [self.logger SensitiveNSObject:[device getIdentifier]]]; channel.inputStream.delegate = device; [channel.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; [channel.inputStream open]; channel.outputStream.delegate = device; [channel.outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; [channel.outputStream open]; @autoreleasepool { do { [[NSRunLoop currentRunLoop] run]; } while (device.peer != nil && [device.peer isConnected]); } }]; [device.l2capThread start]; device.l2capServerHandshakeRunning = TRUE; } @end ================================================ FILE: pkg/ble-driver/BleQueue.h ================================================ // // BleQueue.h // BertyBridgeDemo // // Created by Rémi BARBERO on 03/05/2021. // #import #import "Logger.h" NS_ASSUME_NONNULL_BEGIN #define MAX_TRIES 3 @interface BleQueue : NSObject @property (nonatomic, strong, nonnull) dispatch_queue_t queue; @property (nonatomic, strong, nonnull) Logger *logger; @property (nonatomic, strong, nonnull) NSMutableArray *tasks; @property (readwrite) BOOL taskQueueBusy; @property (readwrite) BOOL isRetrying; @property (readwrite) int nbTries; @property (readwrite) int index; - (instancetype __nullable) init:(dispatch_queue_t)queue logger:(Logger *__nonnull)logger; - (void) add:(void (^__nonnull)(void))block withCallback:(void (^__nullable)(NSError *))callback withDelay:(long)delay; - (void) completedTask:(NSError *__nullable)error; - (void) nextTask; - (void) retryTask; - (void) clear; @end NS_ASSUME_NONNULL_END ================================================ FILE: pkg/ble-driver/BleQueue.m ================================================ // +build darwin,!noproximitytransport // // BleQueue.m // BertyBridgeDemo // // Created by Rémi BARBERO on 03/05/2021. // #import "BleQueue.h" #import "TaskDelay.h" @implementation BleQueue - (instancetype __nullable) init:(dispatch_queue_t)queue logger:(Logger *__nonnull)logger { self = [super init]; if (self) { _tasks = [[NSMutableArray alloc] init]; _queue = [queue retain]; _logger = [logger retain]; } return self; } - (void)dealloc { [_tasks removeAllObjects]; [_tasks release]; [_queue release]; [_logger release]; [super dealloc]; } - (void) add:(void (^__nonnull)(void))block withCallback:(void (^__nullable)(NSError *))callback withDelay:(long)delay { @synchronized (self.tasks) { TaskDelay *task = [[TaskDelay alloc] initWithBlock:block withCallback:callback withDelay:delay withIndex:(self.index++)]; [self.tasks addObject:task]; // add to the end of the array [self.logger d:@"BleQueue: added task at index=%d count=%ld", task.index, [self.tasks count]]; [self nextTask]; [task release]; } } - (void) completedTask:(NSError *__nullable)error { @synchronized (self.tasks) { TaskDelay *currentTask; if ([self.tasks count] == 0) { [self.logger e:@"BleQueue: completedTask error: no task running"]; return ; } currentTask = [self.tasks objectAtIndex:0]; [self.logger d:@"BleQueue: completedTask at index=%d", currentTask.index]; if (currentTask.callback != nil) { dispatch_async(self.queue, ^{ currentTask.callback(error); }); } self.isRetrying = FALSE; self.taskQueueBusy = FALSE; [self.tasks removeObjectAtIndex:0]; [self nextTask]; } } - (void) nextTask { @synchronized (self.tasks) { TaskDelay *nextTask; if (self.taskQueueBusy) { [self.logger d:@"BleQueue: nextTask: another task is running"]; return ; } if ([self.tasks count] == 0 ) { [self.logger d:@"BleQueue: nextTask error: no task queued: count=%ld", [self.tasks count]]; return ; } nextTask = [self.tasks objectAtIndex:0]; [self.logger d:@"BleQueue: nextTask at index=%d with delay=%ld", nextTask.index, nextTask.delay]; self.taskQueueBusy = TRUE; if (!self.isRetrying) { self.nbTries = 0; } dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(nextTask.delay * NSEC_PER_SEC)); dispatch_after(popTime, self.queue, nextTask.block); } } - (void) retryTask { @synchronized (self.tasks) { TaskDelay *currentTask; self.taskQueueBusy = FALSE; if ([self.tasks count] == 0) { [self.logger e:@"BleQueue: retryTask error: no task running"]; } else { currentTask = [self.tasks objectAtIndex:0]; if (self.nbTries >= MAX_TRIES) { [self.logger d:@"BleQueue: max number of tries reached, not retrying operation anymore for task index=%d", currentTask.index]; [self.tasks removeObjectAtIndex:0]; } else { [self.logger d:@"BleQueue: retrying task at index=%d", currentTask.index]; self.isRetrying = TRUE; } } [self nextTask]; } } - (void) clear { @synchronized (self.tasks) { [self.tasks removeAllObjects]; self.taskQueueBusy = FALSE; } } @end ================================================ FILE: pkg/ble-driver/CircularQueue.h ================================================ // // CircularQueue.h // // Created on 9/21/13. // // https://gist.github.com/werediver/5345f91c897b8173e40e // #import # define DEFAULT_CAPACITY 8 @interface CircularQueue : NSObject @property (nonatomic, strong, nonnull) NSMutableArray *data; @property (nonatomic, assign, readwrite) NSInteger capacity; @property (nonatomic, assign, readwrite) NSInteger writeSequence; @property (nonatomic, assign, readwrite) NSInteger readSequence; - (instancetype __nonnull)initWithCapacity:(NSUInteger)capacity; - (void)offer:(id __nonnull)obj; // Enqueue - (id __nonnull)poll; // Get object and unqueue - (id __nonnull)element; // Get object - (void)clean; @end ================================================ FILE: pkg/ble-driver/CircularQueue.m ================================================ // +build darwin,!noproximitytransport #import "CircularQueue.h" @implementation CircularQueue - (instancetype __nonnull)initWithCapacity:(NSUInteger)capacity { self = [super init]; if (self) { _capacity = capacity < 1 ? DEFAULT_CAPACITY : capacity; _data = [[NSMutableArray alloc] initWithCapacity:_capacity]; _writeSequence = -1; _readSequence = 0; for (NSUInteger i = 0; i < _capacity; ++i) { [_data addObject:[NSNull null]]; } } return self; } - (void)dealloc { [_data release]; [super dealloc]; } - (void)offer:(id)obj { if ([self isNotFull]) { NSInteger nextWrite = (self.writeSequence + 1) % self.capacity; [self.data replaceObjectAtIndex:nextWrite withObject:obj]; self.writeSequence++; } else { @throw [[[NSException alloc] initWithName:NSRangeException reason:nil userInfo:nil] autorelease]; } } - (id)poll { if ([self isNotEmpty]) { NSInteger index = self.readSequence % self.capacity; id value = [[self.data objectAtIndex:index] retain]; [self.data replaceObjectAtIndex:index withObject:[NSNull null]]; self.readSequence++; return [value autorelease]; } return [NSNull null]; } - (id)element { if ([self isNotEmpty]) { NSInteger index = self.readSequence % self.capacity; return [self.data objectAtIndex:index]; } return [NSNull null]; } - (void)clean { for (NSUInteger i = 0; i < _capacity; ++i) { [self.data replaceObjectAtIndex:i withObject:[NSNull null]]; } self.readSequence = 0; self.writeSequence = -1; } - (NSInteger)size { return (self.writeSequence - self.readSequence) + 1; } - (BOOL)isEmpty { return self.writeSequence < self.readSequence; } - (BOOL)isFull { return [self size] >= self.capacity; } - (BOOL)isNotEmpty { return ![self isEmpty]; } - (BOOL)isNotFull { return ![self isFull]; } @end ================================================ FILE: pkg/ble-driver/ConnectedPeer.h ================================================ // // ConnectedPeer.h // BertyBridgeDemo // // Created by Rémi BARBERO on 29/04/2021. // #import NS_ASSUME_NONNULL_BEGIN @class BertyDevice; @interface ConnectedPeer : NSObject @property (nonatomic, assign, nullable) BertyDevice *client; @property (nonatomic, assign, nullable) BertyDevice *server; @property (readwrite, getter=isConnected) BOOL connected; - (BOOL)isClientReady; - (BOOL)isServerReady; @end NS_ASSUME_NONNULL_END ================================================ FILE: pkg/ble-driver/ConnectedPeer.m ================================================ // +build darwin,!noproximitytransport // // ConnectedPeer.m // BertyBridgeDemo // // Created by Rémi BARBERO on 29/04/2021. // #import "ConnectedPeer.h" @implementation ConnectedPeer - (BOOL)isClientReady { return self.client != nil; } - (BOOL)isServerReady { return self.server != nil; } @end ================================================ FILE: pkg/ble-driver/CountDownLatch_darwin.h ================================================ // // CountDownLatch.h // ble // // Created by sacha on 22/11/2018. // Copyright © 2018 berty. All rights reserved. // #import #ifndef CountDownLatch_h #define CountDownLatch_h @interface CountDownLatch : NSObject @property (nonatomic, assign, readwrite) NSInteger count; @property (atomic, strong, readwrite) dispatch_semaphore_t semaphore; @property (nonatomic, strong) dispatch_queue_t dispatch_queue; @property (readwrite) BOOL timeout; - (instancetype)initCount:(NSInteger)count; - (void)incrementCount; - (void)countDown; - (void)await; - (void)await:(NSUInteger)timeout withCancelBlock:(void (^)(void))callback; @end #endif ================================================ FILE: pkg/ble-driver/CountDownLatch_darwin.m ================================================ // +build darwin,!noproximitytransport // // CountDownLatch.m // ble // // Created by sacha on 22/11/2018. // Copyright © 2018 berty. All rights reserved. // #import "CountDownLatch_darwin.h" @implementation CountDownLatch - (instancetype)initCount:(NSInteger)count { if (count < 0) { return nil; } self = [super self]; if (self) { _count = count; _semaphore = dispatch_semaphore_create(0); _dispatch_queue = dispatch_queue_create("CountDownLatchQueue", DISPATCH_QUEUE_SERIAL); } return self; } - (void)dealloc { _semaphore = nil; dispatch_release(_dispatch_queue); _dispatch_queue = nil; [super dealloc]; } - (void)incrementCount { dispatch_async(self.dispatch_queue, ^{ self.count++; }); } - (void)countDown { dispatch_async(self.dispatch_queue, ^{ self.count--; if (self.count == 0) { dispatch_semaphore_signal(self.semaphore); } }); } - (void)await { dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER); } - (void)await:(NSUInteger)timeout withCancelBlock:(void (^)(void))callback { self.timeout = TRUE; dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeout * NSEC_PER_SEC)); dispatch_after(popTime, self.dispatch_queue, ^(void){ if (self.timeout) { callback(); dispatch_semaphore_signal(self.semaphore); } }); dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER); self.timeout = FALSE; } @end ================================================ FILE: pkg/ble-driver/Logger.h ================================================ // // Logger.h // BertyBridgeDemo // // Created by Rémi BARBERO on 08/12/2021. // #import #import NS_ASSUME_NONNULL_BEGIN #define SENSITIVE_MASK @"####" // Log levels typedef NS_ENUM(uint8_t, level) { Debug, Info, Warn, Error, }; @interface BLE_Logger : NSObject @property (nonatomic, strong, nonnull) os_log_t logger; @property (readwrite) BOOL showSensitiveData; @property (readwrite) BOOL useExternalLogger; - (instancetype __nonnull)initLocalLoggerWithSubSystem:(const char *)subSystem andCategorie:(const char*)categorie showSensitiveData:(BOOL)showSensitiveData; - (instancetype __nonnull)initWithExternalLoggerAndShowSensitiveData:(BOOL)showSensitiveData; - (void)log:(enum level)level withFormat:(NSString *__nonnull)format withArgs:(va_list)args; - (void)d:(NSString *__nonnull)format, ...; - (void)i:(NSString *__nonnull)format, ...; - (void)e:(NSString *__nonnull)format, ...; - (BOOL)showSensitiveData; - (BOOL)useExternalLogger; - (NSString *__nonnull)SensitiveNSObject:(id __nonnull)data; - (NSString *__nonnull)SensitiveString:(const char *)data; @end @compatibility_alias Logger BLE_Logger; NS_ASSUME_NONNULL_END ================================================ FILE: pkg/ble-driver/Logger.m ================================================ // +build darwin,!noproximitytransport // // Logger.m // BertyBridgeDemo // // Created by Rémi BARBERO on 08/12/2021. // #import #import "Logger.h" #import "BleInterface_darwin.h" @implementation Logger - (instancetype __nonnull)initLocalLoggerWithSubSystem:(const char *)subSystem andCategorie:(const char*)categorie showSensitiveData:(BOOL)showSensitiveData { self = [super init]; if (self) { _logger = os_log_create(subSystem, categorie); _useExternalLogger = FALSE; _showSensitiveData = showSensitiveData; } return self; } - (instancetype __nonnull)initWithExternalLoggerAndShowSensitiveData:(BOOL)showSensitiveData { self = [super init]; if (self) { _logger = nil; _useExternalLogger = TRUE; _showSensitiveData = showSensitiveData; } return self; } - (void)log:(enum level)level withFormat:(NSString *__nonnull)format withArgs:(va_list)args { NSString *message = [[NSString alloc] initWithFormat:format arguments:args]; if (self.useExternalLogger) { BLEBridgeLog(level, message); } else { if (self.logger == nil) { NSLog(@"log error: logger is not set"); } else { uint8_t osLevel; switch (level) { case Debug: osLevel = OS_LOG_TYPE_DEBUG; break ; case Info: osLevel = OS_LOG_TYPE_INFO; break ; case Error: osLevel = OS_LOG_TYPE_ERROR; break ; default: osLevel = OS_LOG_TYPE_DEFAULT; break ; } os_log_with_type(self.logger, osLevel, "%@", message); } } [message release]; } - (void)d:(NSString *__nonnull)format, ... { va_list args; va_start(args, format); [self log:Debug withFormat:format withArgs:args]; va_end(args); } - (void)i:(NSString *__nonnull)format, ... { va_list args; va_start(args, format); [self log:Info withFormat:format withArgs:args]; va_end(args); } - (void)e:(NSString *__nonnull)format, ... { va_list args; va_start(args, format); [self log:Error withFormat:format withArgs:args]; va_end(args); } - (NSString *__nonnull)SensitiveNSObject:(id __nonnull)data { if (self.showSensitiveData) { return [NSString stringWithFormat:@"%@", data]; } else { return SENSITIVE_MASK; } } - (NSString *__nonnull)SensitiveString:(const char *)data { if (data == nil) { return @""; } if (self.showSensitiveData) { return [NSString stringWithFormat:@"%s", data]; } else { return SENSITIVE_MASK; } } @end ================================================ FILE: pkg/ble-driver/PeerManager.h ================================================ // // PeerManager.h // BertyBridgeDemo // // Created by Rémi BARBERO on 29/04/2021. // #import #import "Logger.h" #import "ConnectedPeer.h" NS_ASSUME_NONNULL_BEGIN @class BertyDevice; @interface PeerManager : NSObject @property (nonatomic, strong, nonnull) NSMutableDictionary *connectedPeers; @property (nonatomic, strong, nonnull) Logger *logger; - (instancetype __nonnull)initWithLogger:(Logger *__nonnull)logger; - (ConnectedPeer *__nonnull)getPeer:(NSString *__nonnull) peerID; - (ConnectedPeer *__nullable)registerDevice:(BertyDevice *__nonnull)device withPeerID:(NSString *__nonnull)peerID isClient:(BOOL)isClient; - (void)unregisterDevice:(BertyDevice *)device; - (void)removePeer:(NSString *__nonnull) peerID; - (void)removeAllPeers; @end NS_ASSUME_NONNULL_END ================================================ FILE: pkg/ble-driver/PeerManager.m ================================================ // +build darwin,!noproximitytransport // // PeerManager.m // BertyBridgeDemo // // Created by Rémi BARBERO on 29/04/2021. // #import "PeerManager.h" #import "BleInterface_darwin.h" @implementation PeerManager - (instancetype __nonnull)initWithLogger:(Logger *__nonnull)logger { self = [super init]; if (self) { _logger = [logger retain]; _connectedPeers = [[NSMutableDictionary alloc] init]; } return self; } - (void)dealloc { [_connectedPeers release]; [_logger release]; [super dealloc]; } - (ConnectedPeer *__nonnull)getPeer:(NSString *__nonnull) peerID { [self.logger d:@"getPeer called: peerID=%@", [self.logger SensitiveNSObject:peerID]]; ConnectedPeer *peer; @synchronized (_connectedPeers) { if ((peer = [self.connectedPeers objectForKey:peerID]) != nil) { [self.logger d:@"getPeer: peerID=%@ alread created", [self.logger SensitiveNSObject:peerID]]; return peer; } [self.logger d:@"getPeer: peerID=%@ created", [self.logger SensitiveNSObject:peerID]]; peer = [[ConnectedPeer alloc] init]; [self.connectedPeers setObject:peer forKey:peerID]; [peer release]; return peer; } } - (ConnectedPeer *__nullable)registerDevice:(BertyDevice *__nonnull)device withPeerID:(NSString *__nonnull)peerID isClient:(BOOL)isClient { [self.logger d:@"registerDevice called: identifier=%@ peer=%@ isClient=%d", [self.logger SensitiveNSObject:[device getIdentifier]], [self.logger SensitiveNSObject:peerID], isClient]; ConnectedPeer *peer; @synchronized (_connectedPeers) { peer = [self getPeer:peerID]; if (isClient) { peer.client = device; } else { peer.server = device; } device.peer = peer; peer.connected = TRUE; if (!BLEBridgeHandleFoundPeer(peerID)) { [self.logger e:@"registerDevice error: device=%@ peer=%@: HandleFoundPeer failed", [self.logger SensitiveNSObject:[device getIdentifier]], [self.logger SensitiveNSObject:peerID]]; return NULL; } [device flushCache]; } return peer; } - (void)unregisterDevice:(BertyDevice *)device { [self.logger d:@"unregisterDevice called: device=%@ peerID=%@", [self.logger SensitiveNSObject:[device getIdentifier]], [self.logger SensitiveNSObject:device.remotePeerID]]; ConnectedPeer *peer; @synchronized (_connectedPeers) { if ((peer = [self.connectedPeers objectForKey:device.remotePeerID]) == nil) { [self.logger e:@"unregisterDevice called: device=%@ peerID=%@: peerID not found", [self.logger SensitiveNSObject:[device getIdentifier]], [self.logger SensitiveNSObject:device.remotePeerID]]; return ; } if ([peer isConnected]) { [self.logger d:@"unregisterDevice called: device=%{public}@ peerID=%{public}@: calling HandleLostPeer", [self.logger SensitiveNSObject:[device getIdentifier]], [self.logger SensitiveNSObject:device.remotePeerID]]; BLEBridgeHandleLostPeer(device.remotePeerID); peer.connected = FALSE; } [self removePeer:device.remotePeerID]; } } - (void)removePeer:(NSString *__nonnull) peerID { [self.logger d:@"removePeer called: peerID=%{public}@", [self.logger SensitiveNSObject:peerID]]; @synchronized (_connectedPeers) { [self.connectedPeers removeObjectForKey:peerID]; } } - (void)removeAllPeers { [self.logger d:@"removeAllPeers called"]; @synchronized (_connectedPeers) { [self.connectedPeers removeAllObjects]; } } @end ================================================ FILE: pkg/ble-driver/TaskDelay.h ================================================ // // TaskDelay.h // BertyBridgeDemo // // Created by Rémi BARBERO on 03/05/2021. // #import NS_ASSUME_NONNULL_BEGIN @interface TaskDelay : NSObject @property (nonatomic, copy) void (^block)(void); @property (nonatomic, copy) void (^callback)(NSError *); @property (nonatomic, assign) long delay; @property (nonatomic, assign) int index; - (instancetype __nullable) initWithBlock:(void (^ __nonnull)(void))block withCallback:(void (^__nullable)(NSError *))callback withDelay:(long)delay withIndex:(int)index; @end NS_ASSUME_NONNULL_END ================================================ FILE: pkg/ble-driver/TaskDelay.m ================================================ // +build darwin,!noproximitytransport // // TaskDelay.m // BertyBridgeDemo // // Created by Rémi BARBERO on 03/05/2021. // #import "TaskDelay.h" @implementation TaskDelay - (instancetype __nullable) initWithBlock:(void (^ __nonnull)(void))block withCallback:(void (^__nullable)(NSError *))callback withDelay:(long)delay withIndex:(int)index { self = [super init]; if (self) { _block = [block copy]; _callback = [callback copy]; _delay = delay; _index = index; } return self; } - (void)dealloc { [_block release]; [_callback release]; [super dealloc]; } @end ================================================ FILE: pkg/ble-driver/WriteDataCache.h ================================================ // // WriteDataCache.h // BertyBridgeDemo // // Created by Rémi BARBERO on 03/08/2021. // #import NS_ASSUME_NONNULL_BEGIN @class BertyDevice; @interface WriteDataCache : NSObject @property (nonatomic, strong, nonnull) BertyDevice *device; @property (nonatomic, strong, nonnull) NSData *data; - (instancetype __nonnull) initWithDevice:(BertyDevice *__nonnull)device withData:(NSData *__nonnull)data; @end NS_ASSUME_NONNULL_END ================================================ FILE: pkg/ble-driver/WriteDataCache.m ================================================ // +build darwin,!noproximitytransport // // WriteDataCache.m // BertyBridgeDemo // // Created by Rémi BARBERO on 03/08/2021. // #import "WriteDataCache.h" #import "BertyDevice_darwin.h" @implementation WriteDataCache - (instancetype __nonnull) initWithDevice:(BertyDevice *__nonnull)device withData:(NSData *__nonnull)data { self = [super init]; if (self) { _device = [device retain]; _data = [data retain]; } return self; } - (void)dealloc { [_device release]; [_data release]; [super dealloc]; } @end ================================================ FILE: pkg/ble-driver/bridge_android.go ================================================ //go:build android && !noproximitytransport package ble import ( "go.uber.org/zap" proximity "berty.tech/weshnet/v2/pkg/proximitytransport" ) // Supported is used by main package as default value for enable the BLE driver. // While UI actually enable or not the Java BLE driver. // TODO: remove this when UI will be able to handle this for the first App launching. const Supported = true // Noop implementation for Android // Real driver is given from Java directly here: berty/js/android/app/src/main/java/tech/berty/gobridge/bledriver func NewDriver(logger *zap.Logger) proximity.ProximityDriver { logger = logger.Named("BLE") logger.Info("NewDriver(): Java driver not found") return proximity.NewNoopProximityDriver(ProtocolCode, ProtocolName, DefaultAddr) } ================================================ FILE: pkg/ble-driver/bridge_darwin.go ================================================ //go:build darwin && cgo && !noproximitytransport // +build darwin,cgo,!noproximitytransport package ble /* #cgo CFLAGS: -x objective-c -fno-objc-arc #cgo darwin LDFLAGS: -framework Foundation -framework CoreBluetooth #include #import "BleInterface_darwin.h" #import "Logger.h" */ import "C" import ( "fmt" "os" "unsafe" "go.uber.org/zap" proximity "berty.tech/weshnet/v2/pkg/proximitytransport" ) const Supported = true var gLogger *zap.Logger type Driver struct { protocolCode int protocolName string defaultAddr string } // Driver is a proximity.ProximityDriver var _ proximity.ProximityDriver = (*Driver)(nil) func NewDriver(logger *zap.Logger) proximity.ProximityDriver { if logger == nil { logger = zap.NewNop() } else { logger = logger.Named("BLE") logger.Debug("NewDriver()") C.BLEUseExternalLogger() } gLogger = logger return &Driver{ protocolCode: ProtocolCode, protocolName: ProtocolName, defaultAddr: DefaultAddr, } } //export BLEHandleFoundPeer func BLEHandleFoundPeer(remotePID *C.char) int { // nolint:revive // Need to prefix func name to avoid duplicate symbols between proximity drivers goPID := C.GoString(remotePID) proximity.TransportMapMutex.RLock() t, ok := proximity.TransportMap[ProtocolName] proximity.TransportMapMutex.RUnlock() if !ok { return 0 } if t.HandleFoundPeer(goPID) { return 1 } return 0 } //export BLEHandleLostPeer func BLEHandleLostPeer(remotePID *C.char) { // nolint:revive // Need to prefix func name to avoid duplicate symbols between proximity drivers goPID := C.GoString(remotePID) proximity.TransportMapMutex.RLock() t, ok := proximity.TransportMap[ProtocolName] proximity.TransportMapMutex.RUnlock() if !ok { return } t.HandleLostPeer(goPID) } //export BLEReceiveFromPeer func BLEReceiveFromPeer(remotePID *C.char, payload unsafe.Pointer, length C.int) { // nolint:revive // Need to prefix func name to avoid duplicate symbols between proximity drivers goPID := C.GoString(remotePID) goPayload := C.GoBytes(payload, length) proximity.TransportMapMutex.RLock() t, ok := proximity.TransportMap[ProtocolName] proximity.TransportMapMutex.RUnlock() if !ok { return } t.ReceiveFromPeer(goPID, goPayload) } //export BLELog func BLELog(level C.enum_level, message *C.char) { //nolint:revive if gLogger == nil { fmt.Fprintf(os.Stderr, "logger not found\n") return } goMessage := C.GoString(message) switch level { case C.Debug: gLogger.Debug(goMessage) case C.Info: gLogger.Info(goMessage) case C.Warn: gLogger.Warn(goMessage) case C.Error: gLogger.Error(goMessage) } } func (d *Driver) Start(localPID string) { cPID := C.CString(localPID) defer C.free(unsafe.Pointer(cPID)) C.BLEStart(cPID) } func (d *Driver) Stop() { C.BLEStop() } func (d *Driver) DialPeer(remotePID string) bool { cPID := C.CString(remotePID) defer C.free(unsafe.Pointer(cPID)) return C.BLEDialPeer(cPID) == 1 } func (d *Driver) SendToPeer(remotePID string, payload []byte) bool { cPID := C.CString(remotePID) defer C.free(unsafe.Pointer(cPID)) cPayload := C.CBytes(payload) defer C.free(cPayload) return C.BLESendToPeer(cPID, cPayload, C.int(len(payload))) == 1 } func (d *Driver) CloseConnWithPeer(remotePID string) { cPID := C.CString(remotePID) defer C.free(unsafe.Pointer(cPID)) C.BLECloseConnWithPeer(cPID) } func (d *Driver) ProtocolCode() int { return d.protocolCode } func (d *Driver) ProtocolName() string { return d.protocolName } func (d *Driver) DefaultAddr() string { return d.defaultAddr } ================================================ FILE: pkg/ble-driver/bridge_unsupported.go ================================================ //go:build (!darwin && !android) || noproximitytransport package ble import ( "go.uber.org/zap" proximity "berty.tech/weshnet/v2/pkg/proximitytransport" ) const Supported = false // Noop implementation for platform that are not Darwin func NewDriver(logger *zap.Logger) proximity.ProximityDriver { logger = logger.Named("BLE") logger.Info("NewDriver(): incompatible system") return proximity.NewNoopProximityDriver(ProtocolCode, ProtocolName, DefaultAddr) } ================================================ FILE: pkg/ble-driver/const.go ================================================ package ble const ( DefaultAddr = "/ble/Qmeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" ProtocolCode = 0x0042 ProtocolName = "ble" ) ================================================ FILE: pkg/ble-driver/example_test.go ================================================ package ble_test ================================================ FILE: pkg/ble-driver/init.go ================================================ package ble import ( ma "github.com/multiformats/go-multiaddr" ) func init() { // nolint:gochecknoinits err := ma.AddProtocol(newProtocol()) if err != nil { panic(err) } } ================================================ FILE: pkg/ble-driver/multiaddr.go ================================================ package ble import ( peer "github.com/libp2p/go-libp2p/core/peer" ma "github.com/multiformats/go-multiaddr" ) func newProtocol() ma.Protocol { transcoderMC := ma.NewTranscoderFromFunctions(mcStB, mcBtS, mcVal) return ma.Protocol{ Name: ProtocolName, Code: ProtocolCode, VCode: ma.CodeToVarint(ProtocolCode), Size: -1, Path: false, Transcoder: transcoderMC, } } func mcStB(s string) ([]byte, error) { _, err := peer.Decode(s) if err != nil { return nil, err } return []byte(s), nil } func mcBtS(b []byte) (string, error) { _, err := peer.Decode(string(b)) if err != nil { return "", err } return string(b), nil } func mcVal(b []byte) error { _, err := peer.Decode(string(b)) return err } ================================================ FILE: pkg/cryptoutil/cryptoutil.go ================================================ package cryptoutil import ( "crypto/aes" "crypto/cipher" crand "crypto/rand" "crypto/sha256" "crypto/sha512" "fmt" "filippo.io/edwards25519" "github.com/libp2p/go-libp2p/core/crypto" pb "github.com/libp2p/go-libp2p/core/crypto/pb" "golang.org/x/crypto/ed25519" "golang.org/x/crypto/scrypt" "berty.tech/weshnet/v2/pkg/errcode" ) const ( KeySize = 32 // Key size required by box NonceSize = 24 // Nonce size required by box ScryptIterations = 1 << 15 ScryptR = 8 ScryptP = 1 ScryptKeyLen = 32 ) func ConcatAndHashSha256(slices ...[]byte) *[sha256.Size]byte { var concat []byte for _, slice := range slices { concat = append(concat, slice...) } checksum := sha256.Sum256(concat) return &checksum } func GenerateNonce() (*[NonceSize]byte, error) { var nonce [NonceSize]byte size, err := crand.Read(nonce[:]) if size != NonceSize { err = fmt.Errorf("size read: %d (required %d)", size, NonceSize) } if err != nil { return nil, errcode.ErrCode_ErrCryptoRandomGeneration.Wrap(err) } return &nonce, nil } func GenerateNonceSize(size int) ([]byte, error) { nonce := make([]byte, size) readSize, err := crand.Read(nonce) if readSize != size { err = fmt.Errorf("size read: %d (required %d)", readSize, size) } if err != nil { return nil, errcode.ErrCode_ErrCryptoRandomGeneration.Wrap(err) } return nonce, nil } func NonceSliceToArray(nonceSlice []byte) (*[NonceSize]byte, error) { var nonceArray [NonceSize]byte if l := len(nonceSlice); l != NonceSize { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("invalid nonce size, expected %d bytes, got %d", NonceSize, l)) } copy(nonceArray[:], nonceSlice) return &nonceArray, nil } func KeySliceToArray(keySlice []byte) (*[KeySize]byte, error) { var keyArray [KeySize]byte if l := len(keySlice); l != KeySize { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("unable to convert slice to array, unexpected slice size: %d (expected %d)", l, KeySize)) } copy(keyArray[:], keySlice) return &keyArray, nil } func SeedFromEd25519PrivateKey(key crypto.PrivKey) ([]byte, error) { // Similar to (*ed25519).Seed() if key.Type() != pb.KeyType_Ed25519 { return nil, errcode.ErrCode_ErrInvalidInput } r, err := key.Raw() if err != nil { return nil, errcode.ErrCode_ErrSerialization.Wrap(err) } if len(r) != ed25519.PrivateKeySize { return nil, errcode.ErrCode_ErrInvalidInput } return r[:ed25519.PrivateKeySize-ed25519.PublicKeySize], nil } // EdwardsToMontgomery converts ed25519 priv/pub keys to X25519 keys. func EdwardsToMontgomery(privKey crypto.PrivKey, pubKey crypto.PubKey) (*[32]byte, *[32]byte, error) { mongPriv, err := EdwardsToMontgomeryPriv(privKey) if err != nil { return nil, nil, err } mongPub, err := EdwardsToMontgomeryPub(pubKey) if err != nil { return nil, nil, err } return mongPriv, mongPub, nil } // EdwardsToMontgomeryPub converts ed25519 pub key to X25519 pub key. func EdwardsToMontgomeryPub(pubKey crypto.PubKey) (*[KeySize]byte, error) { var mongPub [KeySize]byte if pubKey.Type() != pb.KeyType_Ed25519 { return nil, errcode.ErrCode_ErrInvalidInput } rawPublicKey, err := pubKey.Raw() if err != nil { return nil, fmt.Errorf("unable to get raw public key: %w", err) } else if len(rawPublicKey) != ed25519.PublicKeySize { return nil, fmt.Errorf("invalid ed25519 public key size: %w", err) } err = PublicKeyToCurve25519(&mongPub, (ed25519.PublicKey)(rawPublicKey)) if err != nil { return nil, fmt.Errorf("unable to get publickey to curve 25519: %w", err) } return &mongPub, nil } // EdwardsToMontgomeryPriv converts ed25519 priv key to X25519 priv key. func EdwardsToMontgomeryPriv(privKey crypto.PrivKey) (*[KeySize]byte, error) { var mongPriv [KeySize]byte if privKey.Type() != pb.KeyType_Ed25519 { return nil, errcode.ErrCode_ErrInvalidInput } rawPrivateKey, err := privKey.Raw() if err != nil { return nil, errcode.ErrCode_ErrSerialization.Wrap(err) } else if len(rawPrivateKey) != ed25519.PrivateKeySize { return nil, errcode.ErrCode_ErrInvalidInput } PrivateKeyToCurve25519(&mongPriv, rawPrivateKey) return &mongPriv, nil } // AESGCMEncrypt use AES+GCM to encrypt plaintext data. // // The generated output will be longer than the original plaintext input. func AESGCMEncrypt(key, data []byte) ([]byte, error) { blockCipher, err := aes.NewCipher(key) if err != nil { return nil, err } gcm, err := cipher.NewGCM(blockCipher) if err != nil { return nil, err } nonce, err := GenerateNonceSize(gcm.NonceSize()) if err != nil { return nil, err } ciphertext := gcm.Seal(nonce, nonce, data, nil) return ciphertext, nil } // AESGCMDecrypt uses AES+GCM to decrypt plaintext data. func AESGCMDecrypt(key, data []byte) ([]byte, error) { blockCipher, err := aes.NewCipher(key) if err != nil { return nil, err } gcm, err := cipher.NewGCM(blockCipher) if err != nil { return nil, err } nonce, ciphertext := data[:gcm.NonceSize()], data[gcm.NonceSize():] plaintext, err := gcm.Open(nil, nonce, ciphertext, nil) if err != nil { return nil, err } return plaintext, nil } // AESCTRStream returns a CTR stream that can be used to produce ciphertext without padding. func AESCTRStream(key, iv []byte) (cipher.Stream, error) { if key == nil || iv == nil { return nil, errcode.ErrCode_ErrInvalidInput } blockCipher, err := aes.NewCipher(key) if err != nil { return nil, err } stream := cipher.NewCTR(blockCipher, iv) return stream, nil } // DeriveKey takes a passphrase of any length and returns a key of fixed size. // // If no salt is provided, a new one will be created and returned. func DeriveKey(passphrase, salt []byte) ([]byte, []byte, error) { if salt == nil { var err error salt, err = GenerateNonceSize(ScryptKeyLen) if err != nil { return nil, nil, err } } key, err := scrypt.Key(passphrase, salt, ScryptIterations, ScryptR, ScryptP, ScryptKeyLen) if err != nil { return nil, nil, err } return key, salt, nil } // PublicKeyToCurve25519 converts an Ed25519 public key into the curve25519 // public key that would be generated from the same private key. func PublicKeyToCurve25519(ret *[32]byte, publicKey ed25519.PublicKey) error { point, err := edwards25519.NewGeneratorPoint().SetBytes(publicKey) if err != nil { return fmt.Errorf("unable to generate point from publicKey: %w", err) } copy(ret[:], point.BytesMontgomery()) return nil } // from: https://github.com/agl/ed25519/blob/5312a61534124124185d41f09206b9fef1d88403/extra25519/extra25519.go#LL16C11-L16C11 // PrivateKeyToCurve25519 converts an ed25519 private key into a corresponding // curve25519 private key such that the resulting curve25519 public key will // equal the result from PublicKeyToCurve25519. func PrivateKeyToCurve25519(ret *[32]byte, privateKey ed25519.PrivateKey) { h := sha512.New() h.Write(privateKey.Seed()) copy(ret[:], h.Sum(nil)) ret[0] &= 248 ret[31] &= 127 ret[31] |= 64 } ================================================ FILE: pkg/cryptoutil/cryptoutil_test.go ================================================ package cryptoutil import ( "bytes" "crypto/aes" "crypto/rand" "testing" "github.com/libp2p/go-libp2p/core/crypto" "github.com/stretchr/testify/require" "golang.org/x/crypto/curve25519" "golang.org/x/crypto/ed25519" "berty.tech/weshnet/v2/pkg/errcode" ) // from: https://github.com/agl/ed25519/blob/5312a61534124124185d41f09206b9fef1d88403/extra25519/extra25519_test.go#L16-L30 func TestCurve25519Conversion(t *testing.T) { public, private, err := ed25519.GenerateKey(rand.Reader) require.NoError(t, err) var curve25519Public, curve25519Public2, curve25519Private [32]byte PrivateKeyToCurve25519(&curve25519Private, private) curve25519.ScalarBaseMult(&curve25519Public, &curve25519Private) err = PublicKeyToCurve25519(&curve25519Public2, public) require.NoError(t, err) require.Truef(t, bytes.Equal(curve25519Public[:], curve25519Public2[:]), "Values didn't match: curve25519 produced %x, conversion produced %x", curve25519Public[:], curve25519Public2[:]) } func TestSeedFromEd25519PrivateKey(t *testing.T) { priv, _, _ := crypto.GenerateECDSAKeyPair(rand.Reader) _, err := SeedFromEd25519PrivateKey(priv) if err != errcode.ErrCode_ErrInvalidInput { t.Error("Should fail with ErrInvalidInput") } priv, _, _ = crypto.GenerateEd25519Key(rand.Reader) _, err = SeedFromEd25519PrivateKey(priv) if err != nil { t.Error(err) } } func TestEdwardsToMontgomeryPub(t *testing.T) { _, pub, _ := crypto.GenerateEd25519Key(rand.Reader) _, err := EdwardsToMontgomeryPub(pub) if err != nil { t.Error(err) } _, pub, _ = crypto.GenerateECDSAKeyPair(rand.Reader) _, err = EdwardsToMontgomeryPub(pub) if err != errcode.ErrCode_ErrInvalidInput { t.Error("Should fail with ErrInvalidInput") } } func TestEdwardsToMontgomeryPriv(t *testing.T) { priv, _, _ := crypto.GenerateEd25519Key(rand.Reader) _, err := EdwardsToMontgomeryPriv(priv) if err != nil { t.Error(err) } priv, _, _ = crypto.GenerateECDSAKeyPair(rand.Reader) _, err = EdwardsToMontgomeryPriv(priv) if err != errcode.ErrCode_ErrInvalidInput { t.Error("Should fail with ErrInvalidInput") } } func TestDeriveKey(t *testing.T) { cases := []struct { passphrase []byte salt []byte }{ {nil, nil}, {[]byte{0x42}, []byte{0x42}}, {nil, []byte{0x42}}, {[]byte{0x42}, nil}, {[]byte("hello world"), []byte("hello world")}, {[]byte("morethan32bytes.................."), []byte("hello world")}, {[]byte("hello world"), []byte("morethan32bytes..................")}, {[]byte("morethan32bytes.................."), []byte("morethan32bytes..................")}, } for _, tc := range cases { t.Run("", func(t *testing.T) { key, salt, err := DeriveKey(tc.passphrase, tc.salt) require.NoError(t, err) require.Equal(t, ScryptKeyLen, len(key)) if tc.salt != nil { require.Equal(t, tc.salt, salt) } require.NotEqual(t, tc.passphrase, key) }) } } func TestAESGCMEncryptDecrypt(t *testing.T) { var ( passphrase = []byte("my priv4te k3y") salt = []byte("my sup3r s3lt") message = []byte("12345678901234567890123456789012") ) key, salt, err := DeriveKey(passphrase, salt) require.NoError(t, err) require.Equal(t, salt, salt) require.NotEqual(t, passphrase, key) require.Equal(t, ScryptKeyLen, len(key)) enc, err := AESGCMEncrypt(key, message) require.NoError(t, err) require.NotEqual(t, message, enc) dec, err := AESGCMDecrypt(key, enc) require.NoError(t, err) require.Equal(t, message, dec) } func TestAESCTRStream(t *testing.T) { var ( passphrase = []byte("my priv4te k3y") message1 = []byte("hello world!") message2 = []byte("hi planet!") enc1 = make([]byte, len(message1)) enc2 = make([]byte, len(message2)) ) // generate nonce with AES' blocksize nonce, err := GenerateNonceSize(aes.BlockSize) require.NoError(t, err) require.NotNil(t, nonce) // derive key for AES key, salt, err := DeriveKey(passphrase, nonce) require.NoError(t, err) require.NotNil(t, key) require.Equal(t, nonce, salt) // encrypt { stream, err := AESCTRStream(key, nonce) require.NoError(t, err) stream.XORKeyStream(enc1, message1) stream.XORKeyStream(enc2, message2) require.NotEqual(t, enc1, message1) require.NotEqual(t, enc2, message2) require.Equal(t, len(enc1), len(message1)) require.Equal(t, len(enc2), len(message2)) } // decrypt { var ( dec1 = make([]byte, len(message1)) dec2 = make([]byte, len(message2)) ) stream, err := AESCTRStream(key, nonce) require.NoError(t, err) stream.XORKeyStream(dec1, enc1) stream.XORKeyStream(dec2, enc2) require.Equal(t, dec1, message1) require.Equal(t, dec2, message2) } // silently failing invalid decrypt { invalidKey, _, err := DeriveKey([]byte("invalid passphrase"), nonce) var ( dec1 = make([]byte, len(message1)) dec2 = make([]byte, len(message2)) ) require.NoError(t, err) require.NotNil(t, invalidKey) stream, err := AESCTRStream(invalidKey, nonce) require.NoError(t, err) stream.XORKeyStream(dec1, enc1) stream.XORKeyStream(dec2, enc2) require.NotEqual(t, dec1, message1) require.NotEqual(t, dec2, message2) require.NotEqual(t, dec1, enc1) require.NotEqual(t, dec2, enc2) require.Equal(t, len(dec1), len(enc1)) require.Equal(t, len(dec2), len(enc2)) } } ================================================ FILE: pkg/cryptoutil/doc.go ================================================ // Package cryptoutil contains generic & stateless crypto helpers. package cryptoutil ================================================ FILE: pkg/cryptoutil/signer_wrapper.go ================================================ package cryptoutil import ( stdcrypto "crypto" "io" libp2p_ci "github.com/libp2p/go-libp2p/core/crypto" ) func NewFuncSigner(key libp2p_ci.PubKey, signer func([]byte) ([]byte, error)) stdcrypto.Signer { return &funcSigner{ pubKey: key, signer: signer, } } type funcSigner struct { pubKey libp2p_ci.PubKey signer func([]byte) ([]byte, error) } func (f *funcSigner) Public() stdcrypto.PublicKey { return f.pubKey } func (f *funcSigner) Sign(_ io.Reader, digest []byte, _ stdcrypto.SignerOpts) (signature []byte, err error) { return f.signer(digest) } ================================================ FILE: pkg/errcode/doc.go ================================================ // Package errcode contains the list of Berty error codes. package errcode ================================================ FILE: pkg/errcode/errcode.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.34.2 // protoc (unknown) // source: errcode/errcode.proto package errcode import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type ErrCode int32 const ( ErrCode_Undefined ErrCode = 0 // default value, should never be set manually ErrCode_TODO ErrCode = 666 // indicates that you plan to create an error later ErrCode_ErrNotImplemented ErrCode = 777 // indicates that a method is not implemented yet ErrCode_ErrInternal ErrCode = 888 // indicates an unknown error (without Code), i.e. in gRPC ErrCode_ErrInvalidInput ErrCode = 100 ErrCode_ErrInvalidRange ErrCode = 101 ErrCode_ErrMissingInput ErrCode = 102 ErrCode_ErrSerialization ErrCode = 103 ErrCode_ErrDeserialization ErrCode = 104 ErrCode_ErrStreamRead ErrCode = 105 ErrCode_ErrStreamWrite ErrCode = 106 ErrCode_ErrStreamTransform ErrCode = 110 ErrCode_ErrStreamSendAndClose ErrCode = 111 ErrCode_ErrStreamHeaderWrite ErrCode = 112 ErrCode_ErrStreamHeaderRead ErrCode = 115 ErrCode_ErrStreamSink ErrCode = 113 ErrCode_ErrStreamCloseAndRecv ErrCode = 114 ErrCode_ErrMissingMapKey ErrCode = 107 ErrCode_ErrDBWrite ErrCode = 108 ErrCode_ErrDBRead ErrCode = 109 ErrCode_ErrDBDestroy ErrCode = 120 ErrCode_ErrDBMigrate ErrCode = 121 ErrCode_ErrDBReplay ErrCode = 122 ErrCode_ErrDBRestore ErrCode = 123 ErrCode_ErrDBOpen ErrCode = 124 ErrCode_ErrDBClose ErrCode = 125 ErrCode_ErrCryptoRandomGeneration ErrCode = 200 ErrCode_ErrCryptoKeyGeneration ErrCode = 201 ErrCode_ErrCryptoNonceGeneration ErrCode = 202 ErrCode_ErrCryptoSignature ErrCode = 203 ErrCode_ErrCryptoSignatureVerification ErrCode = 204 ErrCode_ErrCryptoDecrypt ErrCode = 205 ErrCode_ErrCryptoDecryptPayload ErrCode = 206 ErrCode_ErrCryptoEncrypt ErrCode = 207 ErrCode_ErrCryptoKeyConversion ErrCode = 208 ErrCode_ErrCryptoCipherInit ErrCode = 209 ErrCode_ErrCryptoKeyDerivation ErrCode = 210 ErrCode_ErrMap ErrCode = 300 ErrCode_ErrForEach ErrCode = 301 ErrCode_ErrKeystoreGet ErrCode = 400 ErrCode_ErrKeystorePut ErrCode = 401 ErrCode_ErrNotFound ErrCode = 404 // generic ErrCode_ErrOrbitDBInit ErrCode = 1000 ErrCode_ErrOrbitDBOpen ErrCode = 1001 ErrCode_ErrOrbitDBAppend ErrCode = 1002 ErrCode_ErrOrbitDBDeserialization ErrCode = 1003 ErrCode_ErrOrbitDBStoreCast ErrCode = 1004 ErrCode_ErrHandshakeOwnEphemeralKeyGenSend ErrCode = 1100 ErrCode_ErrHandshakePeerEphemeralKeyRecv ErrCode = 1101 ErrCode_ErrHandshakeRequesterAuthenticateBoxKeyGen ErrCode = 1102 ErrCode_ErrHandshakeResponderAcceptBoxKeyGen ErrCode = 1103 ErrCode_ErrHandshakeRequesterHello ErrCode = 1104 ErrCode_ErrHandshakeResponderHello ErrCode = 1105 ErrCode_ErrHandshakeRequesterAuthenticate ErrCode = 1106 ErrCode_ErrHandshakeResponderAccept ErrCode = 1107 ErrCode_ErrHandshakeRequesterAcknowledge ErrCode = 1108 ErrCode_ErrContactRequestSameAccount ErrCode = 1200 ErrCode_ErrContactRequestContactAlreadyAdded ErrCode = 1201 ErrCode_ErrContactRequestContactBlocked ErrCode = 1202 ErrCode_ErrContactRequestContactUndefined ErrCode = 1203 ErrCode_ErrContactRequestIncomingAlreadyReceived ErrCode = 1204 ErrCode_ErrGroupMemberLogEventOpen ErrCode = 1300 ErrCode_ErrGroupMemberLogEventSignature ErrCode = 1301 ErrCode_ErrGroupMemberUnknownGroupID ErrCode = 1302 ErrCode_ErrGroupSecretOtherDestMember ErrCode = 1303 ErrCode_ErrGroupSecretAlreadySentToMember ErrCode = 1304 ErrCode_ErrGroupInvalidType ErrCode = 1305 ErrCode_ErrGroupMissing ErrCode = 1306 ErrCode_ErrGroupActivate ErrCode = 1307 ErrCode_ErrGroupDeactivate ErrCode = 1308 ErrCode_ErrGroupInfo ErrCode = 1309 ErrCode_ErrGroupUnknown ErrCode = 1310 ErrCode_ErrGroupOpen ErrCode = 1311 ErrCode_ErrMessageKeyPersistencePut ErrCode = 1500 ErrCode_ErrMessageKeyPersistenceGet ErrCode = 1501 ErrCode_ErrServiceReplication ErrCode = 4100 ErrCode_ErrServiceReplicationServer ErrCode = 4101 ErrCode_ErrServiceReplicationMissingEndpoint ErrCode = 4102 ErrCode_ErrServicesDirectory ErrCode = 4200 ErrCode_ErrServicesDirectoryInvalidVerifiedCredentialSubject ErrCode = 4201 ErrCode_ErrServicesDirectoryExistingRecordNotFound ErrCode = 4202 ErrCode_ErrServicesDirectoryRecordLockedAndCantBeReplaced ErrCode = 4203 ErrCode_ErrServicesDirectoryExplicitReplaceFlagRequired ErrCode = 4204 ErrCode_ErrServicesDirectoryInvalidVerifiedCredential ErrCode = 4205 ErrCode_ErrServicesDirectoryExpiredVerifiedCredential ErrCode = 4206 ErrCode_ErrServicesDirectoryInvalidVerifiedCredentialID ErrCode = 4207 ) // Enum value maps for ErrCode. var ( ErrCode_name = map[int32]string{ 0: "Undefined", 666: "TODO", 777: "ErrNotImplemented", 888: "ErrInternal", 100: "ErrInvalidInput", 101: "ErrInvalidRange", 102: "ErrMissingInput", 103: "ErrSerialization", 104: "ErrDeserialization", 105: "ErrStreamRead", 106: "ErrStreamWrite", 110: "ErrStreamTransform", 111: "ErrStreamSendAndClose", 112: "ErrStreamHeaderWrite", 115: "ErrStreamHeaderRead", 113: "ErrStreamSink", 114: "ErrStreamCloseAndRecv", 107: "ErrMissingMapKey", 108: "ErrDBWrite", 109: "ErrDBRead", 120: "ErrDBDestroy", 121: "ErrDBMigrate", 122: "ErrDBReplay", 123: "ErrDBRestore", 124: "ErrDBOpen", 125: "ErrDBClose", 200: "ErrCryptoRandomGeneration", 201: "ErrCryptoKeyGeneration", 202: "ErrCryptoNonceGeneration", 203: "ErrCryptoSignature", 204: "ErrCryptoSignatureVerification", 205: "ErrCryptoDecrypt", 206: "ErrCryptoDecryptPayload", 207: "ErrCryptoEncrypt", 208: "ErrCryptoKeyConversion", 209: "ErrCryptoCipherInit", 210: "ErrCryptoKeyDerivation", 300: "ErrMap", 301: "ErrForEach", 400: "ErrKeystoreGet", 401: "ErrKeystorePut", 404: "ErrNotFound", 1000: "ErrOrbitDBInit", 1001: "ErrOrbitDBOpen", 1002: "ErrOrbitDBAppend", 1003: "ErrOrbitDBDeserialization", 1004: "ErrOrbitDBStoreCast", 1100: "ErrHandshakeOwnEphemeralKeyGenSend", 1101: "ErrHandshakePeerEphemeralKeyRecv", 1102: "ErrHandshakeRequesterAuthenticateBoxKeyGen", 1103: "ErrHandshakeResponderAcceptBoxKeyGen", 1104: "ErrHandshakeRequesterHello", 1105: "ErrHandshakeResponderHello", 1106: "ErrHandshakeRequesterAuthenticate", 1107: "ErrHandshakeResponderAccept", 1108: "ErrHandshakeRequesterAcknowledge", 1200: "ErrContactRequestSameAccount", 1201: "ErrContactRequestContactAlreadyAdded", 1202: "ErrContactRequestContactBlocked", 1203: "ErrContactRequestContactUndefined", 1204: "ErrContactRequestIncomingAlreadyReceived", 1300: "ErrGroupMemberLogEventOpen", 1301: "ErrGroupMemberLogEventSignature", 1302: "ErrGroupMemberUnknownGroupID", 1303: "ErrGroupSecretOtherDestMember", 1304: "ErrGroupSecretAlreadySentToMember", 1305: "ErrGroupInvalidType", 1306: "ErrGroupMissing", 1307: "ErrGroupActivate", 1308: "ErrGroupDeactivate", 1309: "ErrGroupInfo", 1310: "ErrGroupUnknown", 1311: "ErrGroupOpen", 1500: "ErrMessageKeyPersistencePut", 1501: "ErrMessageKeyPersistenceGet", 4100: "ErrServiceReplication", 4101: "ErrServiceReplicationServer", 4102: "ErrServiceReplicationMissingEndpoint", 4200: "ErrServicesDirectory", 4201: "ErrServicesDirectoryInvalidVerifiedCredentialSubject", 4202: "ErrServicesDirectoryExistingRecordNotFound", 4203: "ErrServicesDirectoryRecordLockedAndCantBeReplaced", 4204: "ErrServicesDirectoryExplicitReplaceFlagRequired", 4205: "ErrServicesDirectoryInvalidVerifiedCredential", 4206: "ErrServicesDirectoryExpiredVerifiedCredential", 4207: "ErrServicesDirectoryInvalidVerifiedCredentialID", } ErrCode_value = map[string]int32{ "Undefined": 0, "TODO": 666, "ErrNotImplemented": 777, "ErrInternal": 888, "ErrInvalidInput": 100, "ErrInvalidRange": 101, "ErrMissingInput": 102, "ErrSerialization": 103, "ErrDeserialization": 104, "ErrStreamRead": 105, "ErrStreamWrite": 106, "ErrStreamTransform": 110, "ErrStreamSendAndClose": 111, "ErrStreamHeaderWrite": 112, "ErrStreamHeaderRead": 115, "ErrStreamSink": 113, "ErrStreamCloseAndRecv": 114, "ErrMissingMapKey": 107, "ErrDBWrite": 108, "ErrDBRead": 109, "ErrDBDestroy": 120, "ErrDBMigrate": 121, "ErrDBReplay": 122, "ErrDBRestore": 123, "ErrDBOpen": 124, "ErrDBClose": 125, "ErrCryptoRandomGeneration": 200, "ErrCryptoKeyGeneration": 201, "ErrCryptoNonceGeneration": 202, "ErrCryptoSignature": 203, "ErrCryptoSignatureVerification": 204, "ErrCryptoDecrypt": 205, "ErrCryptoDecryptPayload": 206, "ErrCryptoEncrypt": 207, "ErrCryptoKeyConversion": 208, "ErrCryptoCipherInit": 209, "ErrCryptoKeyDerivation": 210, "ErrMap": 300, "ErrForEach": 301, "ErrKeystoreGet": 400, "ErrKeystorePut": 401, "ErrNotFound": 404, "ErrOrbitDBInit": 1000, "ErrOrbitDBOpen": 1001, "ErrOrbitDBAppend": 1002, "ErrOrbitDBDeserialization": 1003, "ErrOrbitDBStoreCast": 1004, "ErrHandshakeOwnEphemeralKeyGenSend": 1100, "ErrHandshakePeerEphemeralKeyRecv": 1101, "ErrHandshakeRequesterAuthenticateBoxKeyGen": 1102, "ErrHandshakeResponderAcceptBoxKeyGen": 1103, "ErrHandshakeRequesterHello": 1104, "ErrHandshakeResponderHello": 1105, "ErrHandshakeRequesterAuthenticate": 1106, "ErrHandshakeResponderAccept": 1107, "ErrHandshakeRequesterAcknowledge": 1108, "ErrContactRequestSameAccount": 1200, "ErrContactRequestContactAlreadyAdded": 1201, "ErrContactRequestContactBlocked": 1202, "ErrContactRequestContactUndefined": 1203, "ErrContactRequestIncomingAlreadyReceived": 1204, "ErrGroupMemberLogEventOpen": 1300, "ErrGroupMemberLogEventSignature": 1301, "ErrGroupMemberUnknownGroupID": 1302, "ErrGroupSecretOtherDestMember": 1303, "ErrGroupSecretAlreadySentToMember": 1304, "ErrGroupInvalidType": 1305, "ErrGroupMissing": 1306, "ErrGroupActivate": 1307, "ErrGroupDeactivate": 1308, "ErrGroupInfo": 1309, "ErrGroupUnknown": 1310, "ErrGroupOpen": 1311, "ErrMessageKeyPersistencePut": 1500, "ErrMessageKeyPersistenceGet": 1501, "ErrServiceReplication": 4100, "ErrServiceReplicationServer": 4101, "ErrServiceReplicationMissingEndpoint": 4102, "ErrServicesDirectory": 4200, "ErrServicesDirectoryInvalidVerifiedCredentialSubject": 4201, "ErrServicesDirectoryExistingRecordNotFound": 4202, "ErrServicesDirectoryRecordLockedAndCantBeReplaced": 4203, "ErrServicesDirectoryExplicitReplaceFlagRequired": 4204, "ErrServicesDirectoryInvalidVerifiedCredential": 4205, "ErrServicesDirectoryExpiredVerifiedCredential": 4206, "ErrServicesDirectoryInvalidVerifiedCredentialID": 4207, } ) func (x ErrCode) Enum() *ErrCode { p := new(ErrCode) *p = x return p } func (x ErrCode) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (ErrCode) Descriptor() protoreflect.EnumDescriptor { return file_errcode_errcode_proto_enumTypes[0].Descriptor() } func (ErrCode) Type() protoreflect.EnumType { return &file_errcode_errcode_proto_enumTypes[0] } func (x ErrCode) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use ErrCode.Descriptor instead. func (ErrCode) EnumDescriptor() ([]byte, []int) { return file_errcode_errcode_proto_rawDescGZIP(), []int{0} } type ErrDetails struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Codes []ErrCode `protobuf:"varint,1,rep,packed,name=codes,proto3,enum=weshnet.errcode.ErrCode" json:"codes,omitempty"` } func (x *ErrDetails) Reset() { *x = ErrDetails{} if protoimpl.UnsafeEnabled { mi := &file_errcode_errcode_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ErrDetails) String() string { return protoimpl.X.MessageStringOf(x) } func (*ErrDetails) ProtoMessage() {} func (x *ErrDetails) ProtoReflect() protoreflect.Message { mi := &file_errcode_errcode_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ErrDetails.ProtoReflect.Descriptor instead. func (*ErrDetails) Descriptor() ([]byte, []int) { return file_errcode_errcode_proto_rawDescGZIP(), []int{0} } func (x *ErrDetails) GetCodes() []ErrCode { if x != nil { return x.Codes } return nil } var File_errcode_errcode_proto protoreflect.FileDescriptor var file_errcode_errcode_proto_rawDesc = []byte{ 0x0a, 0x15, 0x65, 0x72, 0x72, 0x63, 0x6f, 0x64, 0x65, 0x2f, 0x65, 0x72, 0x72, 0x63, 0x6f, 0x64, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x65, 0x72, 0x72, 0x63, 0x6f, 0x64, 0x65, 0x22, 0x3c, 0x0a, 0x0a, 0x45, 0x72, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x2e, 0x0a, 0x05, 0x63, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x65, 0x72, 0x72, 0x63, 0x6f, 0x64, 0x65, 0x2e, 0x45, 0x72, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x52, 0x05, 0x63, 0x6f, 0x64, 0x65, 0x73, 0x2a, 0xdb, 0x13, 0x0a, 0x07, 0x45, 0x72, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x0d, 0x0a, 0x09, 0x55, 0x6e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x04, 0x54, 0x4f, 0x44, 0x4f, 0x10, 0x9a, 0x05, 0x12, 0x16, 0x0a, 0x11, 0x45, 0x72, 0x72, 0x4e, 0x6f, 0x74, 0x49, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x65, 0x64, 0x10, 0x89, 0x06, 0x12, 0x10, 0x0a, 0x0b, 0x45, 0x72, 0x72, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x10, 0xf8, 0x06, 0x12, 0x13, 0x0a, 0x0f, 0x45, 0x72, 0x72, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x10, 0x64, 0x12, 0x13, 0x0a, 0x0f, 0x45, 0x72, 0x72, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x10, 0x65, 0x12, 0x13, 0x0a, 0x0f, 0x45, 0x72, 0x72, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x10, 0x66, 0x12, 0x14, 0x0a, 0x10, 0x45, 0x72, 0x72, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x10, 0x67, 0x12, 0x16, 0x0a, 0x12, 0x45, 0x72, 0x72, 0x44, 0x65, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x10, 0x68, 0x12, 0x11, 0x0a, 0x0d, 0x45, 0x72, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x61, 0x64, 0x10, 0x69, 0x12, 0x12, 0x0a, 0x0e, 0x45, 0x72, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x57, 0x72, 0x69, 0x74, 0x65, 0x10, 0x6a, 0x12, 0x16, 0x0a, 0x12, 0x45, 0x72, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x10, 0x6e, 0x12, 0x19, 0x0a, 0x15, 0x45, 0x72, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x65, 0x6e, 0x64, 0x41, 0x6e, 0x64, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x10, 0x6f, 0x12, 0x18, 0x0a, 0x14, 0x45, 0x72, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x57, 0x72, 0x69, 0x74, 0x65, 0x10, 0x70, 0x12, 0x17, 0x0a, 0x13, 0x45, 0x72, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x61, 0x64, 0x10, 0x73, 0x12, 0x11, 0x0a, 0x0d, 0x45, 0x72, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x69, 0x6e, 0x6b, 0x10, 0x71, 0x12, 0x19, 0x0a, 0x15, 0x45, 0x72, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x41, 0x6e, 0x64, 0x52, 0x65, 0x63, 0x76, 0x10, 0x72, 0x12, 0x14, 0x0a, 0x10, 0x45, 0x72, 0x72, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x4d, 0x61, 0x70, 0x4b, 0x65, 0x79, 0x10, 0x6b, 0x12, 0x0e, 0x0a, 0x0a, 0x45, 0x72, 0x72, 0x44, 0x42, 0x57, 0x72, 0x69, 0x74, 0x65, 0x10, 0x6c, 0x12, 0x0d, 0x0a, 0x09, 0x45, 0x72, 0x72, 0x44, 0x42, 0x52, 0x65, 0x61, 0x64, 0x10, 0x6d, 0x12, 0x10, 0x0a, 0x0c, 0x45, 0x72, 0x72, 0x44, 0x42, 0x44, 0x65, 0x73, 0x74, 0x72, 0x6f, 0x79, 0x10, 0x78, 0x12, 0x10, 0x0a, 0x0c, 0x45, 0x72, 0x72, 0x44, 0x42, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x65, 0x10, 0x79, 0x12, 0x0f, 0x0a, 0x0b, 0x45, 0x72, 0x72, 0x44, 0x42, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x10, 0x7a, 0x12, 0x10, 0x0a, 0x0c, 0x45, 0x72, 0x72, 0x44, 0x42, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x10, 0x7b, 0x12, 0x0d, 0x0a, 0x09, 0x45, 0x72, 0x72, 0x44, 0x42, 0x4f, 0x70, 0x65, 0x6e, 0x10, 0x7c, 0x12, 0x0e, 0x0a, 0x0a, 0x45, 0x72, 0x72, 0x44, 0x42, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x10, 0x7d, 0x12, 0x1e, 0x0a, 0x19, 0x45, 0x72, 0x72, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x52, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x10, 0xc8, 0x01, 0x12, 0x1b, 0x0a, 0x16, 0x45, 0x72, 0x72, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x4b, 0x65, 0x79, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x10, 0xc9, 0x01, 0x12, 0x1d, 0x0a, 0x18, 0x45, 0x72, 0x72, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x10, 0xca, 0x01, 0x12, 0x17, 0x0a, 0x12, 0x45, 0x72, 0x72, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x10, 0xcb, 0x01, 0x12, 0x23, 0x0a, 0x1e, 0x45, 0x72, 0x72, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x10, 0xcc, 0x01, 0x12, 0x15, 0x0a, 0x10, 0x45, 0x72, 0x72, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x10, 0xcd, 0x01, 0x12, 0x1c, 0x0a, 0x17, 0x45, 0x72, 0x72, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x10, 0xce, 0x01, 0x12, 0x15, 0x0a, 0x10, 0x45, 0x72, 0x72, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x10, 0xcf, 0x01, 0x12, 0x1b, 0x0a, 0x16, 0x45, 0x72, 0x72, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x4b, 0x65, 0x79, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x10, 0xd0, 0x01, 0x12, 0x18, 0x0a, 0x13, 0x45, 0x72, 0x72, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, 0x49, 0x6e, 0x69, 0x74, 0x10, 0xd1, 0x01, 0x12, 0x1b, 0x0a, 0x16, 0x45, 0x72, 0x72, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x4b, 0x65, 0x79, 0x44, 0x65, 0x72, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x10, 0xd2, 0x01, 0x12, 0x0b, 0x0a, 0x06, 0x45, 0x72, 0x72, 0x4d, 0x61, 0x70, 0x10, 0xac, 0x02, 0x12, 0x0f, 0x0a, 0x0a, 0x45, 0x72, 0x72, 0x46, 0x6f, 0x72, 0x45, 0x61, 0x63, 0x68, 0x10, 0xad, 0x02, 0x12, 0x13, 0x0a, 0x0e, 0x45, 0x72, 0x72, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x47, 0x65, 0x74, 0x10, 0x90, 0x03, 0x12, 0x13, 0x0a, 0x0e, 0x45, 0x72, 0x72, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x50, 0x75, 0x74, 0x10, 0x91, 0x03, 0x12, 0x10, 0x0a, 0x0b, 0x45, 0x72, 0x72, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x94, 0x03, 0x12, 0x13, 0x0a, 0x0e, 0x45, 0x72, 0x72, 0x4f, 0x72, 0x62, 0x69, 0x74, 0x44, 0x42, 0x49, 0x6e, 0x69, 0x74, 0x10, 0xe8, 0x07, 0x12, 0x13, 0x0a, 0x0e, 0x45, 0x72, 0x72, 0x4f, 0x72, 0x62, 0x69, 0x74, 0x44, 0x42, 0x4f, 0x70, 0x65, 0x6e, 0x10, 0xe9, 0x07, 0x12, 0x15, 0x0a, 0x10, 0x45, 0x72, 0x72, 0x4f, 0x72, 0x62, 0x69, 0x74, 0x44, 0x42, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x10, 0xea, 0x07, 0x12, 0x1e, 0x0a, 0x19, 0x45, 0x72, 0x72, 0x4f, 0x72, 0x62, 0x69, 0x74, 0x44, 0x42, 0x44, 0x65, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x10, 0xeb, 0x07, 0x12, 0x18, 0x0a, 0x13, 0x45, 0x72, 0x72, 0x4f, 0x72, 0x62, 0x69, 0x74, 0x44, 0x42, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x61, 0x73, 0x74, 0x10, 0xec, 0x07, 0x12, 0x27, 0x0a, 0x22, 0x45, 0x72, 0x72, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x4f, 0x77, 0x6e, 0x45, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x47, 0x65, 0x6e, 0x53, 0x65, 0x6e, 0x64, 0x10, 0xcc, 0x08, 0x12, 0x25, 0x0a, 0x20, 0x45, 0x72, 0x72, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x50, 0x65, 0x65, 0x72, 0x45, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x63, 0x76, 0x10, 0xcd, 0x08, 0x12, 0x2f, 0x0a, 0x2a, 0x45, 0x72, 0x72, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x72, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x42, 0x6f, 0x78, 0x4b, 0x65, 0x79, 0x47, 0x65, 0x6e, 0x10, 0xce, 0x08, 0x12, 0x29, 0x0a, 0x24, 0x45, 0x72, 0x72, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x64, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x42, 0x6f, 0x78, 0x4b, 0x65, 0x79, 0x47, 0x65, 0x6e, 0x10, 0xcf, 0x08, 0x12, 0x1f, 0x0a, 0x1a, 0x45, 0x72, 0x72, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x72, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x10, 0xd0, 0x08, 0x12, 0x1f, 0x0a, 0x1a, 0x45, 0x72, 0x72, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x64, 0x65, 0x72, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x10, 0xd1, 0x08, 0x12, 0x26, 0x0a, 0x21, 0x45, 0x72, 0x72, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x72, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x10, 0xd2, 0x08, 0x12, 0x20, 0x0a, 0x1b, 0x45, 0x72, 0x72, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x64, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x10, 0xd3, 0x08, 0x12, 0x25, 0x0a, 0x20, 0x45, 0x72, 0x72, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x72, 0x41, 0x63, 0x6b, 0x6e, 0x6f, 0x77, 0x6c, 0x65, 0x64, 0x67, 0x65, 0x10, 0xd4, 0x08, 0x12, 0x21, 0x0a, 0x1c, 0x45, 0x72, 0x72, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x61, 0x6d, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x10, 0xb0, 0x09, 0x12, 0x29, 0x0a, 0x24, 0x45, 0x72, 0x72, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x41, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x41, 0x64, 0x64, 0x65, 0x64, 0x10, 0xb1, 0x09, 0x12, 0x24, 0x0a, 0x1f, 0x45, 0x72, 0x72, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x10, 0xb2, 0x09, 0x12, 0x26, 0x0a, 0x21, 0x45, 0x72, 0x72, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x55, 0x6e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x10, 0xb3, 0x09, 0x12, 0x2d, 0x0a, 0x28, 0x45, 0x72, 0x72, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x41, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x10, 0xb4, 0x09, 0x12, 0x1f, 0x0a, 0x1a, 0x45, 0x72, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4f, 0x70, 0x65, 0x6e, 0x10, 0x94, 0x0a, 0x12, 0x24, 0x0a, 0x1f, 0x45, 0x72, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x10, 0x95, 0x0a, 0x12, 0x21, 0x0a, 0x1c, 0x45, 0x72, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x44, 0x10, 0x96, 0x0a, 0x12, 0x22, 0x0a, 0x1d, 0x45, 0x72, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x4f, 0x74, 0x68, 0x65, 0x72, 0x44, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x10, 0x97, 0x0a, 0x12, 0x26, 0x0a, 0x21, 0x45, 0x72, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x41, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x53, 0x65, 0x6e, 0x74, 0x54, 0x6f, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x10, 0x98, 0x0a, 0x12, 0x18, 0x0a, 0x13, 0x45, 0x72, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x54, 0x79, 0x70, 0x65, 0x10, 0x99, 0x0a, 0x12, 0x14, 0x0a, 0x0f, 0x45, 0x72, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x10, 0x9a, 0x0a, 0x12, 0x15, 0x0a, 0x10, 0x45, 0x72, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x10, 0x9b, 0x0a, 0x12, 0x17, 0x0a, 0x12, 0x45, 0x72, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x44, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x10, 0x9c, 0x0a, 0x12, 0x11, 0x0a, 0x0c, 0x45, 0x72, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x10, 0x9d, 0x0a, 0x12, 0x14, 0x0a, 0x0f, 0x45, 0x72, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x9e, 0x0a, 0x12, 0x11, 0x0a, 0x0c, 0x45, 0x72, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4f, 0x70, 0x65, 0x6e, 0x10, 0x9f, 0x0a, 0x12, 0x20, 0x0a, 0x1b, 0x45, 0x72, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4b, 0x65, 0x79, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x50, 0x75, 0x74, 0x10, 0xdc, 0x0b, 0x12, 0x20, 0x0a, 0x1b, 0x45, 0x72, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4b, 0x65, 0x79, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x47, 0x65, 0x74, 0x10, 0xdd, 0x0b, 0x12, 0x1a, 0x0a, 0x15, 0x45, 0x72, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x10, 0x84, 0x20, 0x12, 0x20, 0x0a, 0x1b, 0x45, 0x72, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x10, 0x85, 0x20, 0x12, 0x29, 0x0a, 0x24, 0x45, 0x72, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x10, 0x86, 0x20, 0x12, 0x19, 0x0a, 0x14, 0x45, 0x72, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x10, 0xe8, 0x20, 0x12, 0x39, 0x0a, 0x34, 0x45, 0x72, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x10, 0xe9, 0x20, 0x12, 0x2f, 0x0a, 0x2a, 0x45, 0x72, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x45, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0xea, 0x20, 0x12, 0x36, 0x0a, 0x31, 0x45, 0x72, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x41, 0x6e, 0x64, 0x43, 0x61, 0x6e, 0x74, 0x42, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x64, 0x10, 0xeb, 0x20, 0x12, 0x34, 0x0a, 0x2f, 0x45, 0x72, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x45, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x10, 0xec, 0x20, 0x12, 0x32, 0x0a, 0x2d, 0x45, 0x72, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x10, 0xed, 0x20, 0x12, 0x32, 0x0a, 0x2d, 0x45, 0x72, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x64, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x10, 0xee, 0x20, 0x12, 0x34, 0x0a, 0x2f, 0x45, 0x72, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x49, 0x44, 0x10, 0xef, 0x20, 0x42, 0x23, 0x5a, 0x21, 0x62, 0x65, 0x72, 0x74, 0x79, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x2f, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2f, 0x76, 0x32, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x65, 0x72, 0x72, 0x63, 0x6f, 0x64, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( file_errcode_errcode_proto_rawDescOnce sync.Once file_errcode_errcode_proto_rawDescData = file_errcode_errcode_proto_rawDesc ) func file_errcode_errcode_proto_rawDescGZIP() []byte { file_errcode_errcode_proto_rawDescOnce.Do(func() { file_errcode_errcode_proto_rawDescData = protoimpl.X.CompressGZIP(file_errcode_errcode_proto_rawDescData) }) return file_errcode_errcode_proto_rawDescData } var file_errcode_errcode_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_errcode_errcode_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_errcode_errcode_proto_goTypes = []any{ (ErrCode)(0), // 0: weshnet.errcode.ErrCode (*ErrDetails)(nil), // 1: weshnet.errcode.ErrDetails } var file_errcode_errcode_proto_depIdxs = []int32{ 0, // 0: weshnet.errcode.ErrDetails.codes:type_name -> weshnet.errcode.ErrCode 1, // [1:1] is the sub-list for method output_type 1, // [1:1] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name } func init() { file_errcode_errcode_proto_init() } func file_errcode_errcode_proto_init() { if File_errcode_errcode_proto != nil { return } if !protoimpl.UnsafeEnabled { file_errcode_errcode_proto_msgTypes[0].Exporter = func(v any, i int) any { switch v := v.(*ErrDetails); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_errcode_errcode_proto_rawDesc, NumEnums: 1, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_errcode_errcode_proto_goTypes, DependencyIndexes: file_errcode_errcode_proto_depIdxs, EnumInfos: file_errcode_errcode_proto_enumTypes, MessageInfos: file_errcode_errcode_proto_msgTypes, }.Build() File_errcode_errcode_proto = out.File file_errcode_errcode_proto_rawDesc = nil file_errcode_errcode_proto_goTypes = nil file_errcode_errcode_proto_depIdxs = nil } ================================================ FILE: pkg/errcode/error.go ================================================ package errcode import ( "fmt" "io" "slices" "golang.org/x/xerrors" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) // WithCode defines an error that can be used by helpers of this package. type WithCode interface { error Code() ErrCode } // Codes returns a list of wrapped codes func Codes(err error) []ErrCode { if err == nil { return nil } codes := []ErrCode{} if st := getGRPCStatus(err); st != nil { return codesFromGRPCStatus(st) } if code := currentCode(err); code != -1 { codes = []ErrCode{code} } if cause := genericCause(err); cause != nil { causeCodes := Codes(cause) if len(causeCodes) > 0 { codes = append(codes, Codes(cause)...) } } return codes } // Has returns true if one of the error is or contains (wraps) an expected errcode func Has(err error, code WithCode) bool { codeCode := code.Code() return slices.Contains(Codes(err), codeCode) } // Is returns true if the top-level error (it doesn't unwrap it) is actually an ErrCode of the same value func Is(err error, code WithCode) bool { return currentCode(err) == code.Code() } // currentCode returns the code of the actual error without trying to unwrap it, or -1. func currentCode(err error) ErrCode { if err == nil { return -1 } if typed, ok := err.(WithCode); ok { return typed.Code() } if st := getGRPCStatus(err); st != nil { codes := codesFromGRPCStatus(st) if len(codes) > 0 { return codes[0] } return -1 } return -1 } // Code walks the passed error and returns the code of the first ErrCode met, or -1. func Code(err error) ErrCode { if err == nil { return -1 } if code := currentCode(err); code != -1 { return code } if cause := genericCause(err); cause != nil { return Code(cause) } return -1 } // LastCode walks the passed error and returns the code of the latest ErrCode, or -1. func LastCode(err error) ErrCode { if err == nil { return -1 } if cause := genericCause(err); cause != nil { if ret := LastCode(cause); ret != -1 { return ret } } if st := getGRPCStatus(err); st != nil { codes := codesFromGRPCStatus(st) if len(codes) > 0 { return codes[len(codes)-1] } return -1 } return currentCode(err) } func genericCause(err error) error { type causer interface{ Cause() error } type wrapper interface{ Unwrap() error } if causer, ok := err.(causer); ok { return causer.Cause() } if wrapper, ok := err.(wrapper); ok { return wrapper.Unwrap() } return nil } // // Error // func (e ErrCode) Error() string { name, ok := ErrCode_name[int32(e)] if ok { return fmt.Sprintf("%s(#%d)", name, e) } return fmt.Sprintf("UNKNOWN_ERRCODE(#%d)", e) } func (e ErrCode) Code() ErrCode { return e } func (e ErrCode) Wrap(inner error) WithCode { return wrappedError{ code: e, inner: inner, frame: xerrors.Caller(1), } } func (e ErrCode) GRPCStatus() *status.Status { code := grpcCodeFromWithCode(e) st, _ := status.New(code, e.Error()).WithDetails( &ErrDetails{Codes: Codes(e)}, ) return st } // // ConfigurableError // type wrappedError struct { code ErrCode inner error frame xerrors.Frame } func (e wrappedError) Error() string { return fmt.Sprintf("%s: %v", e.code, e.inner) } func (e wrappedError) Code() ErrCode { return e.code } // Cause returns the inner error (github.com/pkg/errors) func (e wrappedError) Cause() error { return e.inner } // Unwrap returns the inner error (go1.13) func (e wrappedError) Unwrap() error { return e.inner } func (e wrappedError) GRPCStatus() *status.Status { code := grpcCodeFromWithCode(e) st, _ := status.New(code, e.Error()).WithDetails( &ErrDetails{Codes: Codes(e)}, ) return st } func (e wrappedError) Format(f fmt.State, c rune) { xerrors.FormatError(e, f, c) if f.Flag('+') { _, _ = io.WriteString(f, "\n") if sub := genericCause(e); sub != nil { if typed, ok := sub.(wrappedError); ok { sub = lightWrappedError{wrappedError: typed} } formatter, ok := sub.(fmt.Formatter) if ok { formatter.Format(f, c) } } } } func (e wrappedError) FormatError(p xerrors.Printer) error { p.Print(e.Error()) if p.Detail() { e.frame.Format(p) } return nil } // // light wrapper (used to make prettier (less verbose) stacks) // type lightWrappedError struct { wrappedError deepness int } func (e lightWrappedError) Error() string { return "" } func (e lightWrappedError) Format(f fmt.State, c rune) { xerrors.FormatError(e, f, c) if f.Flag('+') { _, _ = io.WriteString(f, "\n") if sub := genericCause(e); sub != nil { if typed, ok := sub.(wrappedError); ok { sub = lightWrappedError{wrappedError: typed, deepness: e.deepness + 1} } formatter, ok := sub.(fmt.Formatter) if ok { formatter.Format(f, c) } } } } func (e lightWrappedError) FormatError(p xerrors.Printer) error { p.Printf("#%d", e.deepness+1) e.frame.Format(p) return nil } // // gRPC helpers // func codesFromGRPCStatus(st *status.Status) []ErrCode { details := st.Details() for _, detail := range details { if typed, ok := detail.(*ErrDetails); ok { return typed.Codes } } return nil } func grpcCodeFromWithCode(WithCode) codes.Code { // here, we can do a big switch case if we plan to make accurate gRPC codes // but we probably don't care return codes.Unavailable } type gRPCStatus interface{ GRPCStatus() *status.Status } func getGRPCStatus(err error) *status.Status { if _, ok := err.(WithCode); !ok { if typed, ok := err.(gRPCStatus); ok { return typed.GRPCStatus() } } return nil } ================================================ FILE: pkg/errcode/error_test.go ================================================ package errcode import ( "fmt" "testing" "github.com/pkg/errors" "github.com/stretchr/testify/assert" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) var ( errStdHello = fmt.Errorf("hello") errCodeUndef = ErrCode(65530) // simulate a client receiving an error generated from a more recent API ) func TestError(t *testing.T) { // test instance var ( _ ErrCode = ErrCode_ErrNotImplemented _ error = ErrCode_ErrNotImplemented _ WithCode = ErrCode_ErrNotImplemented ) // table-driven tests tests := []struct { name string input error expectedString string expectedCause error expectedCode ErrCode expectedLastCode ErrCode expectedCodes []ErrCode has777 bool has888 bool is777 bool is888 bool }{ {"ErrInternal", ErrCode_ErrInternal, "ErrInternal(#888)", ErrCode_ErrInternal, 888, 888, []ErrCode{888}, false, true, false, true}, {"ErrNotImplemented", ErrCode_ErrNotImplemented, "ErrNotImplemented(#777)", ErrCode_ErrNotImplemented, 777, 777, []ErrCode{777}, true, false, true, false}, {"ErrNotImplemented.Wrap(ErrInternal)", ErrCode_ErrNotImplemented.Wrap(ErrCode_ErrInternal), "ErrNotImplemented(#777): ErrInternal(#888)", ErrCode_ErrInternal, 777, 888, []ErrCode{777, 888}, true, true, true, false}, {"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}, {"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}, {"ErrNotImplemented.Wrap(errStdHello)", ErrCode_ErrNotImplemented.Wrap(errStdHello), "ErrNotImplemented(#777): hello", errStdHello, 777, 777, []ErrCode{777}, true, false, true, false}, {"errCodeUndef", errCodeUndef, "UNKNOWN_ERRCODE(#65530)", errCodeUndef, 65530, 65530, []ErrCode{65530}, false, false, false, false}, {"errStdHello", errStdHello, "hello", errStdHello, -1, -1, []ErrCode{}, false, false, false, false}, {"nil", nil, "", nil, -1, -1, nil, false, false, false, false}, {`errors.Wrap(ErrNotImplemented,blah)`, errors.Wrap(ErrCode_ErrNotImplemented, "blah"), "blah: ErrNotImplemented(#777)", ErrCode_ErrNotImplemented, 777, 777, []ErrCode{777}, true, false, false, false}, {`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}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { assert.Equal(t, test.expectedString, fmt.Sprint(test.input)) assert.Equal(t, test.expectedCode, Code(test.input)) assert.Equal(t, test.expectedLastCode, LastCode(test.input)) assert.Equal(t, test.expectedCause, errors.Cause(test.input)) assert.Equal(t, test.expectedCodes, Codes(test.input)) assert.Equal(t, test.has777, Has(test.input, ErrCode_ErrNotImplemented)) assert.Equal(t, test.has888, Has(test.input, ErrCode_ErrInternal)) assert.Equal(t, test.is777, Is(test.input, ErrCode_ErrNotImplemented)) assert.Equal(t, test.is888, Is(test.input, ErrCode_ErrInternal)) }) } } func TestStatus(t *testing.T) { tests := []struct { name string input error has777 bool has888 bool expectedGrpcCode codes.Code hasGrpcStatus bool }{ {"ErrInternal", ErrCode_ErrInternal, false, true, codes.Unavailable, true}, {"ErrNotImplemented", ErrCode_ErrNotImplemented, true, false, codes.Unavailable, true}, {"ErrNotImplemented.Wrap(ErrInternal)", ErrCode_ErrNotImplemented.Wrap(ErrCode_ErrInternal), true, true, codes.Unavailable, true}, {"ErrNotImplemented.Wrap(ErrInternal.Wrap(ErrNotImplemented))", ErrCode_ErrNotImplemented.Wrap(ErrCode_ErrInternal.Wrap(ErrCode_ErrNotImplemented)), true, true, codes.Unavailable, true}, {"ErrNotImplemented.Wrap(ErrInternal.Wrap(TODO))", ErrCode_ErrNotImplemented.Wrap(ErrCode_ErrInternal.Wrap(ErrCode_TODO)), true, true, codes.Unavailable, true}, {"ErrNotImplemented.Wrap(ErrInternal.Wrap(errStdHello))", ErrCode_ErrNotImplemented.Wrap(ErrCode_ErrInternal.Wrap(errStdHello)), true, true, codes.Unavailable, true}, {"ErrNotImplemented.Wrap(errStdHello)", ErrCode_ErrNotImplemented.Wrap(errStdHello), true, false, codes.Unavailable, true}, {"errCodeUndef", errCodeUndef, false, false, codes.Unavailable, true}, {"errStdHello", errStdHello, false, false, codes.Unknown, false}, {"nil", nil, false, false, codes.OK, true}, {`errors.Wrap(ErrNotImplemented,blah)`, errors.Wrap(ErrCode_ErrNotImplemented, "blah"), true, false, codes.Unavailable, true}, {`errors.Wrap(ErrNotImplemented.Wrap(ErrInternal},blah)`, errors.Wrap(ErrCode_ErrNotImplemented.Wrap(ErrCode_ErrInternal), "blah"), true, true, codes.Unavailable, true}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { st, ok := status.FromError(test.input) assert.Equal(t, test.hasGrpcStatus, ok) if test.input != nil { assert.Error(t, st.Err()) assert.Equal(t, st.Message(), test.input.Error()) } if test.hasGrpcStatus { stErr := st.Err() if test.input != nil { assert.NotNil(t, st) assert.Error(t, stErr) } assert.Equal(t, st.Code().String(), test.expectedGrpcCode.String()) assert.Equal(t, Code(test.input), Code(stErr)) assert.Equal(t, LastCode(test.input), LastCode(stErr)) assert.Equal(t, LastCode(test.input), LastCode(stErr)) assert.Equal(t, Codes(test.input), Codes(stErr)) } }) } } ================================================ FILE: pkg/errcode/stdproto.go ================================================ package errcode // nolint:staticcheck // cannot use the new protobuf API while keeping gogoproto // nolint:gochecknoinits // cannot avoid using this init func // nolint:staticcheck // cannot use the new protobuf API while keeping gogoproto func init() { // the goal of this file is to register types on non-gogo proto (required by status.Details) // proto.RegisterEnum("weshnet.errcode.ErrCode", ErrCode_name, ErrCode_value) // nolint:staticcheck // cannot use the new protobuf API while keeping gogoproto // proto.RegisterType((*ErrDetails)(nil), "weshnet.errcode.ErrDetails") // nolint:staticcheck // cannot use the new protobuf API while keeping gogoproto } ================================================ FILE: pkg/grpcutil/buf_listener.go ================================================ package grpcutil import ( "context" "net" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/test/bufconn" ) type BufListener struct { *bufconn.Listener } func NewBufListener(sz int) *BufListener { return &BufListener{ Listener: bufconn.Listen(sz), } } func (bl *BufListener) dialer(context.Context, string) (net.Conn, error) { return bl.Dial() } func (bl *BufListener) NewClientConn(_ context.Context, opts ...grpc.DialOption) (*grpc.ClientConn, error) { mendatoryOpts := []grpc.DialOption{ grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithContextDialer(bl.dialer), // set pipe dialer } return grpc.NewClient("passthrough://buf", append(opts, mendatoryOpts...)...) } ================================================ FILE: pkg/grpcutil/doc.go ================================================ // Package grpcutil contains gRPC lazy codecs, messages and a buf-based listener. package grpcutil ================================================ FILE: pkg/grpcutil/simple_auth.go ================================================ package grpcutil import ( "context" "google.golang.org/grpc/credentials" ) const headerAuthorize = "authorization" var _ credentials.PerRPCCredentials = (*unsecureSimpleAuthAccess)(nil) // unsecureSimpleAuthAccess supplies PerRPCCredentials from a given token. type unsecureSimpleAuthAccess struct { token string scheme string } // NewUnsecureSimpleAuthAccess constructs the PerRPCCredentials using a given token. func NewUnsecureSimpleAuthAccess(scheme, token string) credentials.PerRPCCredentials { return &unsecureSimpleAuthAccess{token: token, scheme: scheme} } // nolint:revive func (sa *unsecureSimpleAuthAccess) GetRequestMetadata(_ context.Context, uri ...string) (map[string]string, error) { return map[string]string{ headerAuthorize: "bearer " + sa.token, }, nil } func (unsecureSimpleAuthAccess) RequireTransportSecurity() bool { return false } ================================================ FILE: pkg/grpcutil/simple_auth_test.go ================================================ package grpcutil import ( "context" "net" "testing" grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware" grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/auth" grpc_ctxtags "github.com/grpc-ecosystem/go-grpc-middleware/tags" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/grpc" "google.golang.org/grpc/codes" pb "google.golang.org/grpc/examples/helloworld/helloworld" "google.golang.org/grpc/status" ) const testGoodToken = "hellobuddy" func validateToken(token string) bool { return token == testGoodToken } // testAuthFunc is used by a middleware to authenticate requests func testAuthFunc(ctx context.Context) (context.Context, error) { token, err := grpc_auth.AuthFromMD(ctx, "bearer") if err != nil { return nil, err } if !validateToken(token) { return nil, status.Errorf(codes.Unauthenticated, "invalid auth token: %s", token) } tags := grpc_ctxtags.Extract(ctx).Set("auth.secret", token) newCtx := grpc_ctxtags.SetInContext(ctx, tags) return newCtx, nil } func SayHelloAuthenticated(ctx context.Context, request *pb.HelloRequest) (*pb.HelloReply, error) { return &pb.HelloReply{Message: "pong authenticated"}, nil } func TestSimpleAuth(t *testing.T) { cases := []struct { name string token string assertFunc assert.ErrorAssertionFunc }{ {name: "Authenticated", token: testGoodToken, assertFunc: assert.NoError}, {name: "Unauthenticated", token: "badtoken", assertFunc: assert.Error}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() grpcServer := grpc.NewServer( grpc_middleware.WithUnaryServerChain( grpc_auth.UnaryServerInterceptor(testAuthFunc), ), grpc_middleware.WithStreamServerChain( grpc_auth.StreamServerInterceptor(testAuthFunc), ), ) pb.RegisterGreeterService(grpcServer, &pb.GreeterService{SayHello: SayHelloAuthenticated}) l, err := net.Listen("tcp", "127.0.0.1:0") require.NoError(t, err) defer l.Close() cc, err := grpc.DialContext(ctx, l.Addr().String(), []grpc.DialOption{ grpc.WithPerRPCCredentials(NewUnsecureSimpleAuthAccess("bearer", tc.token)), grpc.WithInsecure(), // TODO: remove this, enforce security }...) require.NoError(t, err) go grpcServer.Serve(l) client := pb.NewGreeterClient(cc) res, err := client.SayHello(ctx, &pb.HelloRequest{}) if tc.assertFunc(t, err) && err == nil { assert.Equal(t, "pong authenticated", res.Message) } }) } } ================================================ FILE: pkg/ipfsutil/collector_bandwidth.go ================================================ package ipfsutil import ( metrics "github.com/libp2p/go-libp2p/core/metrics" prometheus "github.com/prometheus/client_golang/prometheus" ) var ( protocolsBandwidthInDesc = prometheus.NewDesc( prometheus.BuildFQName("ipfs", "bandwidth", "in"), "protocol bandwidth in", []string{"protocol_id"}, nil, ) protocolsBandwidthOutDesc = prometheus.NewDesc( prometheus.BuildFQName("ipfs", "bandwidth", "out"), "protocol bandwidth out", []string{"protocol_id"}, nil, ) ) // BandwidthCollector is a prometheus.Collector var _ prometheus.Collector = (*BandwidthCollector)(nil) type BandwidthCollector struct { reporter *metrics.BandwidthCounter } func NewBandwidthCollector(reporter *metrics.BandwidthCounter) *BandwidthCollector { return &BandwidthCollector{reporter} } func (bc *BandwidthCollector) Collect(cmetric chan<- prometheus.Metric) { for p, s := range bc.reporter.GetBandwidthByProtocol() { if p == "" { continue } cmetric <- prometheus.MustNewConstMetric( protocolsBandwidthInDesc, prometheus.GaugeValue, s.RateIn, string(p)) cmetric <- prometheus.MustNewConstMetric( protocolsBandwidthOutDesc, prometheus.GaugeValue, s.RateOut, string(p)) } } func (bc *BandwidthCollector) Describe(ch chan<- *prometheus.Desc) { prometheus.DescribeByCollect(bc, ch) } ================================================ FILE: pkg/ipfsutil/collector_host.go ================================================ package ipfsutil import ( host "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/network" "github.com/libp2p/go-libp2p/core/protocol" ma "github.com/multiformats/go-multiaddr" prometheus "github.com/prometheus/client_golang/prometheus" ) var ( protocolsStreamsSumDesc = prometheus.NewDesc( prometheus.BuildFQName("ipfs", "host", "open_stream"), "number of open stream for this protocol", []string{"protocol_id"}, nil, ) connsSumOpts = prometheus.GaugeOpts{ Name: prometheus.BuildFQName("ipfs", "host", "open_connection"), Help: "number of opened connections", } // protocolStreamDurationOpts = prometheus.HistogramOpts{ // Name: prometheus.BuildFQName("ipfs", "host", "stream_duration"), // Help: "stream duration", // Buckets: prometheus.LinearBuckets(0, 10, 6), // } ) const UnknownProtocol = "UnknownProtocol" type HostCollector struct { host host.Host connsCollector prometheus.Gauge // streamsCollector *prometheus.HistogramVec } func NewHostCollector(h host.Host) *HostCollector { gconns := prometheus.NewGauge(connsSumOpts) // hstreams := prometheus.NewHistogramVec(protocolStreamDurationOpts, []string{"protocol_id"}) cc := &HostCollector{ host: h, connsCollector: gconns, // streamsCollector: hstreams, } h.Network().Notify(cc) return cc } func (cc *HostCollector) Collect(cmetric chan<- prometheus.Metric) { cc.connsCollector.Collect(cmetric) // cc.streamsCollector.Collect(cmetric) streamsMap := make(map[protocol.ID]int) for _, c := range cc.host.Network().Conns() { for _, s := range c.GetStreams() { if s.Protocol() != "" { streamsMap[s.Protocol()]++ } else { streamsMap[UnknownProtocol]++ } } } for p, ns := range streamsMap { cmetric <- prometheus.MustNewConstMetric( protocolsStreamsSumDesc, prometheus.GaugeValue, float64(ns), string(p)) } } func (cc *HostCollector) Describe(ch chan<- *prometheus.Desc) { ch <- protocolsStreamsSumDesc cc.connsCollector.Describe(ch) // cc.streamsCollector.Describe(ch) } func (cc *HostCollector) Listen(network.Network, ma.Multiaddr) {} func (cc *HostCollector) ListenClose(network.Network, ma.Multiaddr) {} func (cc *HostCollector) Connected(network.Network, network.Conn) { cc.connsCollector.Inc() } func (cc *HostCollector) Disconnected(network.Network, network.Conn) { cc.connsCollector.Dec() } func (cc *HostCollector) OpenedStream(network.Network, network.Stream) {} func (cc *HostCollector) ClosedStream(network.Network, network.Stream) { // elpased := time.Since(s.Stat().Opened) // cc.streamsCollector.WithLabelValues(string(s.Protocol())).Observe(elpased.Seconds()) } ================================================ FILE: pkg/ipfsutil/conn_logger.go ================================================ package ipfsutil import ( "context" "time" "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/network" "github.com/libp2p/go-libp2p/core/peer" ma "github.com/multiformats/go-multiaddr" "go.uber.org/zap" "berty.tech/weshnet/v2/pkg/logutil" ) var ignoredTags = map[string]bool{ "kbucket": true, "relay": true, "relay-hop-stream": true, } type connLogger struct { host host.Host logger *zap.Logger } func EnableConnLogger(ctx context.Context, logger *zap.Logger, h host.Host) { notifee := &connLogger{ host: h, logger: logger.Named("conn_logger"), } h.Network().Notify(notifee) go func() { <-ctx.Done() h.Network().StopNotify(notifee) }() } func (cl *connLogger) getPeerTags(p peer.ID) []string { if tagInfo := cl.host.ConnManager().GetTagInfo(p); tagInfo != nil { var tags []string for tag := range tagInfo.Tags { if !ignoredTags[tag] { tags = append(tags, tag) } } if len(tags) > 0 { return tags } } return nil } func (cl *connLogger) Listen(_ network.Network, m ma.Multiaddr) { cl.logger.Debug("Listener opened", logutil.PrivateString("Multiaddr", m.String())) } func (cl *connLogger) ListenClose(_ network.Network, m ma.Multiaddr) { cl.logger.Debug("Listener closed", logutil.PrivateString("Multiaddr", m.String())) } func (cl *connLogger) Connected(_ network.Network, c network.Conn) { // Wait 10 ms until the peer has been tagged by orbit-db go func() { <-time.After(10 * time.Millisecond) if tags := cl.getPeerTags(c.RemotePeer()); tags != nil { cl.logger.Info("Connected", logutil.PrivateString("peer", c.RemotePeer().String()), logutil.PrivateString("to", c.LocalMultiaddr().String()), logutil.PrivateString("from", c.RemoteMultiaddr().String()), logutil.PrivateStrings("tags", tags), ) } }() } func (cl *connLogger) Disconnected(_ network.Network, c network.Conn) { if tags := cl.getPeerTags(c.RemotePeer()); tags != nil { cl.logger.Info("Disconnected", logutil.PrivateString("peer", c.RemotePeer().String()), logutil.PrivateString("to", c.LocalMultiaddr().String()), logutil.PrivateString("from", c.RemoteMultiaddr().String()), logutil.PrivateStrings("tags", tags), ) } } func (cl *connLogger) OpenedStream(_ network.Network, s network.Stream) { if tags := cl.getPeerTags(s.Conn().RemotePeer()); tags != nil { cl.logger.Debug("Stream opened", logutil.PrivateString("peer", s.Conn().RemotePeer().String()), logutil.PrivateString("to", s.Conn().LocalMultiaddr().String()), logutil.PrivateString("from", s.Conn().RemoteMultiaddr().String()), logutil.PrivateString("protocol", string(s.Protocol())), logutil.PrivateStrings("tags", tags), ) } } func (cl *connLogger) ClosedStream(_ network.Network, s network.Stream) { if tags := cl.getPeerTags(s.Conn().RemotePeer()); tags != nil { cl.logger.Debug("Stream closed", logutil.PrivateString("peer", s.Conn().RemotePeer().String()), logutil.PrivateString("to", s.Conn().LocalMultiaddr().String()), logutil.PrivateString("from", s.Conn().RemoteMultiaddr().String()), logutil.PrivateString("protocol", string(s.Protocol())), logutil.PrivateStrings("tags", tags), ) } } ================================================ FILE: pkg/ipfsutil/conn_manager.go ================================================ package ipfsutil import ( "fmt" "sync" "github.com/libp2p/go-libp2p/core/connmgr" "github.com/libp2p/go-libp2p/core/event" "github.com/libp2p/go-libp2p/core/peer" "go.uber.org/zap" ) type TypeTagAction int const ( TypeTagActionTag TypeTagAction = iota TypeTagActionUntag TypeTagActionUpsert ) type EvtPeerTag struct { Kind TypeTagAction Peer peer.ID Tag string Diff int Total int } var _ connmgr.ConnManager = (*BertyConnManager)(nil) // keep track of peer of interest type BertyConnManager struct { connmgr.ConnManager logger *zap.Logger tagEmitter event.Emitter muMarked sync.RWMutex marked map[peer.ID]int } func NewBertyConnManager(logger *zap.Logger, cm connmgr.ConnManager) *BertyConnManager { return &BertyConnManager{ ConnManager: cm, logger: logger, marked: make(map[peer.ID]int), } } func (c *BertyConnManager) RegisterEventBus(bus event.Bus) (err error) { if c.tagEmitter == nil { c.tagEmitter, err = bus.Emitter(new(EvtPeerTag)) } else { err = fmt.Errorf("event emitter already registered") } return err } func (c *BertyConnManager) GetPeerScore(p peer.ID) (score int, exist bool) { c.muMarked.RLock() score, exist = c.marked[p] c.muMarked.RUnlock() return } // TagPeer tags a peer with a string, associating a weight with the tag. func (c *BertyConnManager) TagPeer(p peer.ID, tag string, score int) { c.ConnManager.TagPeer(p, tag, score) old, total := c.computePeerScore(p) if old != total && c.tagEmitter != nil { evt := EvtPeerTag{ Kind: TypeTagActionTag, Peer: p, Tag: tag, Diff: total - old, Total: total, } if err := c.tagEmitter.Emit(evt); err != nil { c.logger.Error("unable to emit tag event", zap.Error(err)) } } } // Untag removes the tagged value from the peer. func (c *BertyConnManager) UntagPeer(p peer.ID, tag string) { c.ConnManager.UntagPeer(p, tag) old, total := c.computePeerScore(p) if old != total && c.tagEmitter != nil { evt := EvtPeerTag{ Kind: TypeTagActionUntag, Peer: p, Tag: tag, Diff: total - old, Total: total, } if err := c.tagEmitter.Emit(evt); err != nil { c.logger.Error("unable to emit tag event", zap.Error(err)) } } } // UpsertTag updates an existing tag or inserts a new one. // The connection manager calls the upsert function supplying the current // value of the tag (or zero if inexistent). The return value is used as // the new value of the tag. func (c *BertyConnManager) UpsertTag(p peer.ID, tag string, upsert func(int) int) { c.ConnManager.UpsertTag(p, tag, upsert) old, total := c.computePeerScore(p) if old != total && c.tagEmitter != nil { evt := EvtPeerTag{ Kind: TypeTagActionUpsert, Peer: p, Tag: tag, Diff: total - old, Total: total, } if err := c.tagEmitter.Emit(evt); err != nil { c.logger.Error("unable to emit tag event", zap.Error(err)) } } } func (c *BertyConnManager) computePeerScore(p peer.ID) (old, newScore int) { c.muMarked.Lock() old = c.marked[p] if info := c.ConnManager.GetTagInfo(p); info != nil { if newScore = info.Value; newScore > 0 { c.marked[p] = newScore } else { delete(c.marked, p) } } c.muMarked.Unlock() return } ================================================ FILE: pkg/ipfsutil/consts.go ================================================ package ipfsutil const ( // svc ams 1 DefaultP2PRdvpMaddr = "/ip4/51.15.25.224/udp/4040/quic-v1/p2p/12D3KooWHhDBv6DJJ4XDWjzEXq6sVNEs6VuxsV1WyBBEhPENHzcZ" // svc ams 1 DefaultP2PStaticRelay = "/ip4/51.15.25.224/udp/6363/quic-v1/p2p/12D3KooWAHcEz4K5XAgRDav9fLuhiRY2wuXip385EmT5RoRkCmjr" ) ================================================ FILE: pkg/ipfsutil/doc.go ================================================ // Package ipfsutil contains helpers around IPFS (logging, datastore, networking, core API, ...). package ipfsutil ================================================ FILE: pkg/ipfsutil/extended_core_api.go ================================================ package ipfsutil import ( ipfs_core "github.com/ipfs/kubo/core" ipfs_coreapi "github.com/ipfs/kubo/core/coreapi" coreiface "github.com/ipfs/kubo/core/coreiface" "github.com/libp2p/go-libp2p/core/connmgr" ipfs_host "github.com/libp2p/go-libp2p/core/host" ) type ConnMgr interface { connmgr.ConnManager } type ExtendedCoreAPI interface { coreiface.CoreAPI ipfs_host.Host ConnMgr() ConnMgr } type extendedCoreAPI struct { coreiface.CoreAPI ipfs_host.Host } func (e *extendedCoreAPI) ConnMgr() ConnMgr { return e.Host.ConnManager() } func NewExtendedCoreAPI(host ipfs_host.Host, api coreiface.CoreAPI) ExtendedCoreAPI { return &extendedCoreAPI{ CoreAPI: api, Host: host, } } func NewExtendedCoreAPIFromNode(node *ipfs_core.IpfsNode) (ExtendedCoreAPI, error) { api, err := ipfs_coreapi.NewCoreAPI(node) if err != nil { return nil, err } return NewExtendedCoreAPI(node.PeerHost, api), nil } ================================================ FILE: pkg/ipfsutil/helpers.go ================================================ package ipfsutil import ( "context" "errors" "fmt" "io" "sync" "time" "github.com/libp2p/go-libp2p/core/network" "github.com/libp2p/go-libp2p/core/peer" ma "github.com/multiformats/go-multiaddr" madns "github.com/multiformats/go-multiaddr-dns" "go.uber.org/multierr" "go.uber.org/zap" "berty.tech/weshnet/v2/pkg/logutil" ) func ParseAndResolveIpfsAddr(ctx context.Context, addr string) (*peer.AddrInfo, error) { maddr, err := ma.NewMultiaddr(addr) if err != nil { return nil, err } if !madns.Matches(maddr) { return peer.AddrInfoFromP2pAddr(maddr) } addrs, err := madns.Resolve(ctx, maddr) if err != nil { return nil, err } if len(addrs) == 0 { return nil, errors.New("fail to resolve the multiaddr:" + maddr.String()) } var info peer.AddrInfo for _, addr := range addrs { taddr, id := peer.SplitAddr(addr) if id == "" { // not an ipfs addr, skipping. continue } switch info.ID { case "": info.ID = id case id: default: return nil, fmt.Errorf( "ambiguous maddr %s could refer to %s or %s", maddr, info.ID, id, ) } info.Addrs = append(info.Addrs, taddr) } return &info, nil } func ParseAndResolveMaddrs(ctx context.Context, logger *zap.Logger, addrs []string) ([]*peer.AddrInfo, error) { // Resolve all addresses outPeersUnmatched := make([]*peer.AddrInfo, len(addrs)) var ( errs error outLock sync.Mutex wg sync.WaitGroup ) wg.Add(len(addrs)) for i, v := range addrs { go func(j int, addr string) { defer wg.Done() rdvpeer, err := ParseAndResolveIpfsAddr(ctx, addr) if err != nil { outLock.Lock() defer outLock.Unlock() errs = multierr.Append(errs, err) return } addrStrings := make([]string, len(rdvpeer.Addrs)) for i, maddr := range rdvpeer.Addrs { addrStrings[i] = maddr.String() } logger.Debug("rdvp peer resolved addrs", logutil.PrivateString("input", addr), // logutil.PrivateString("ID", rdvpeer.ID.Pretty()), logutil.PrivateStrings("addrs", addrStrings), ) outPeersUnmatched[j] = rdvpeer }(i, v) } wg.Wait() if errs != nil { return nil, errs } // Match peers by ID outPeersMatched := make(map[peer.ID][]ma.Multiaddr) for _, v := range outPeersUnmatched { outPeersMatched[v.ID] = append(outPeersMatched[v.ID], v.Addrs...) } // Create the ultimate *peer.AddrInfo var outPeers []*peer.AddrInfo for id, maddrs := range outPeersMatched { outPeers = append(outPeers, &peer.AddrInfo{ ID: id, Addrs: maddrs, }) } return outPeers, nil } var ErrExpectedEOF = errors.New("red data when expecting EOF") const DefaultCloseTimeout = time.Second * 5 func FullClose(s network.Stream) error { // Start the close. err := s.CloseWrite() if err != nil { return err } // We don't want to wait indefinitely _ = s.SetDeadline(time.Now().Add(DefaultCloseTimeout)) // Trying with a long slice to fetch `n`. n, err := s.Read([]byte{0}) if n > 0 || err == nil { _ = s.Reset() return ErrExpectedEOF } if err == io.EOF { return nil } _ = s.Reset() return err } ================================================ FILE: pkg/ipfsutil/helpers_test.go ================================================ package ipfsutil import ( "bytes" "context" "encoding/hex" "errors" "fmt" "io" mrand "math/rand" "testing" "time" p2p "github.com/libp2p/go-libp2p" "github.com/libp2p/go-libp2p/core/crypto" "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/network" "github.com/libp2p/go-libp2p/core/peerstore" "github.com/libp2p/go-libp2p/core/pnet" tcp "github.com/libp2p/go-libp2p/p2p/transport/tcp" "moul.io/srand" ) const closeTestPid = "/testing/close/0.1.0" // TestFullClose creates 2 hosts (a and b). a will dial b and will then try to close the connection using FullClose. // b will play various scenario to check that it's working fine. func TestFullClose(t *testing.T) { // Creating ctx ctx, cancel := context.WithCancel(context.Background()) defer cancel() var a, b host.Host { // Creating a private network seed, err := srand.Secure() if err != nil { t.Fatalf("failed to fetch secure source: %s", err) } prov := mrand.New(mrand.NewSource(seed)) pskKey := make([]byte, 32) // math/rand#Rand.Read can't ever fail nor return an n != len(buf) _, _ = prov.Read(pskKey) // Generating PSK key, pulled from https://github.com/Kubuxu/go-ipfs-swarm-key-gen/blob/0ee739ec6d322bc1892999882e4738270e97b181/ipfs-swarm-key-gen/main.go#L15-L17 psk, err := pnet.DecodeV1PSK(bytes.NewReader([]byte("/key/swarm/psk/1.0.0/\n/base16/\n" + hex.EncodeToString(pskKey)))) if err != nil { t.Fatalf("failed to create PSK: %s", err) } // Creating the hosts priv, _, err := crypto.GenerateEd25519Key(prov) if err != nil { t.Fatalf("failed to generate A's private key: %s", err) } a, err = p2p.New(p2p.DisableRelay(), p2p.Transport(tcp.NewTCPTransport), p2p.Identity(priv), p2p.PrivateNetwork(psk), p2p.ListenAddrStrings("/ip4/127.0.0.1/tcp/0"), ) if err != nil { t.Fatalf("failed to create host A: %s", err) } priv, _, err = crypto.GenerateEd25519Key(prov) if err != nil { t.Fatalf("failed to generate B's private key: %s", err) } b, err = p2p.New(p2p.DisableRelay(), p2p.Transport(tcp.NewTCPTransport), p2p.Identity(priv), p2p.PrivateNetwork(psk), p2p.ListenAddrStrings("/ip4/127.0.0.1/tcp/0"), ) if err != nil { t.Fatalf("failed to create host B: %s", err) } } // Adding the hosts together a.Peerstore().SetAddrs(b.ID(), b.Addrs(), peerstore.PermanentAddrTTL) b.Peerstore().SetAddrs(a.ID(), a.Addrs(), peerstore.PermanentAddrTTL) // First scenario, regular close { errcb := make(chan error) b.SetStreamHandler(closeTestPid, func(s network.Stream) { n, err := s.Read([]byte{0}) // Trying to read, should io.EOF if n == 0 && err == io.EOF { // Good err = s.Close() // Trying to close ourself if err == nil { errcb <- io.EOF // Perfect return } errcb <- fmt.Errorf("error closing after EOF: %w", err) return } if err != nil { errcb <- fmt.Errorf("error not EOF while reading: %w", err) return } // n > 0 errcb <- errors.New("n > 0, expected EOF") }) errca := make(chan error) // Dialing, we expect a fast close. go func() { s, err := a.NewStream(ctx, b.ID(), closeTestPid) if err != nil { errca <- fmt.Errorf("failed to create stream: %w", err) return } errca <- FullClose(s) }() timec := time.After(DefaultCloseTimeout / 2) // Fast close must resolve in max half of the timeout time. var done uint = 2 for done > 0 { select { case err := <-errca: if err == nil || err == io.EOF { done-- continue } t.Fatalf("error for A while fast close: %s", err) case err := <-errcb: if err == nil || err == io.EOF { done-- continue } t.Fatalf("error for B while fast close: %s", err) case <-timec: t.Fatal("fast close took too long.") } } } // Second scenario, regular timeout { errcb := make(chan error) var gs network.Stream // Prevent running the terminator b.SetStreamHandler(closeTestPid, func(s network.Stream) { gs = s // Thread unsafe if we have concurrent streams incoming, shouldn't happen thx to the pnet. _, err := s.Read([]byte{0}) errcb <- err }) errca := make(chan error) // Dialing, we expect a timeout. go func() { s, err := a.NewStream(ctx, b.ID(), closeTestPid) if err != nil { errca <- fmt.Errorf("failed to create stream: %w", err) return } errca <- FullClose(s) }() timec := time.After(DefaultCloseTimeout + time.Second) // Timeout must resolve in timeout time + 1s. var done uint = 2 for done > 0 { select { case err := <-errca: // i/o deadline reached must be checked msg. if err == nil || err.Error() == "i/o deadline reached" { done-- continue } t.Fatalf("error for A while slow close: %s", err) case err := <-errcb: if err == nil || err == io.EOF { done-- continue } t.Fatalf("error for B while slow close: %s", err) case <-timec: t.Fatal("slow close took too long.") } } _ = gs // Prevent running the GC Terminator. } } ================================================ FILE: pkg/ipfsutil/keystore_datastore.go ================================================ package ipfsutil import ( "context" datastore "github.com/ipfs/go-datastore" keystore "github.com/ipfs/go-ipfs-keystore" "github.com/libp2p/go-libp2p/core/crypto" "berty.tech/weshnet/v2/pkg/errcode" ) type datastoreKeystore struct { ds datastore.Datastore } func (k *datastoreKeystore) Has(name string) (bool, error) { return k.ds.Has(context.TODO(), datastore.NewKey(name)) } func (k *datastoreKeystore) Put(name string, key crypto.PrivKey) error { bytes, err := crypto.MarshalPrivateKey(key) if err != nil { return err } return k.ds.Put(context.TODO(), datastore.NewKey(name), bytes) } func (k *datastoreKeystore) Get(name string) (crypto.PrivKey, error) { bytes, err := k.ds.Get(context.TODO(), datastore.NewKey(name)) if err == datastore.ErrNotFound { return nil, keystore.ErrNoSuchKey } else if err != nil { return nil, err } return crypto.UnmarshalPrivateKey(bytes) } func (k *datastoreKeystore) Delete(name string) error { return k.ds.Delete(context.TODO(), datastore.NewKey(name)) } func (k *datastoreKeystore) List() ([]string, error) { return nil, errcode.ErrCode_ErrNotImplemented } func NewDatastoreKeystore(ds datastore.Datastore) keystore.Keystore { return &datastoreKeystore{ ds: ds, } } ================================================ FILE: pkg/ipfsutil/lifecycle.go ================================================ package ipfsutil import ( "context" "fmt" "sync" "sync/atomic" "time" "github.com/libp2p/go-libp2p/core/connmgr" host "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/p2p/host/eventbus" "github.com/libp2p/go-libp2p/p2p/protocol/ping" "go.uber.org/zap" "berty.tech/weshnet/v2/pkg/lifecycle" "berty.tech/weshnet/v2/pkg/logutil" "berty.tech/weshnet/v2/pkg/netmanager" ) var ( ConnLifecycleGracePeriod = time.Second ConnLifecyclePingTimeout = time.Second * 5 ConnPeerOfInterestMinScore = 20 ) type ConnLifecycle struct { connmgr.ConnManager rootCtx context.Context logger *zap.Logger peering *PeeringService ps *ping.PingService h host.Host lm *lifecycle.Manager } func NewConnLifecycle(ctx context.Context, logger *zap.Logger, h host.Host, ps *PeeringService, lm *lifecycle.Manager, net *netmanager.NetManager) (*ConnLifecycle, error) { cl := &ConnLifecycle{ peering: ps, rootCtx: ctx, logger: logger, ps: ping.NewPingService(h), h: h, lm: lm, } // start peer of interest monitoring process if err := cl.monitorPeerOfInterest(ctx); err != nil { return nil, err } // start app state monitoring process go cl.monitorAppState(ctx) cl.logger.Debug("lifecycle conn started") go func() { currentState := net.GetCurrentState() for { ok, _ := net.WaitForStateChange(ctx, ¤tState, netmanager.ConnectivityStateChanged) if !ok { return } currentState = net.GetCurrentState() if net.GetCurrentState().State == netmanager.ConnectivityStateOn { go cl.dropUnavailableConn() } } }() return cl, nil } func (cl *ConnLifecycle) monitorAppState(ctx context.Context) { currentState := lifecycle.StateActive for { start := time.Now() if !cl.lm.WaitForStateChange(ctx, currentState) { return } currentState = cl.lm.GetCurrentState() if time.Since(start) <= ConnLifecycleGracePeriod { continue } switch currentState { case lifecycle.StateInactive: cl.logger.Debug("inactive mode") case lifecycle.StateActive: cl.logger.Debug("active mode") go cl.dropUnavailableConn() } } } func (cl *ConnLifecycle) dropUnavailableConn() { cl.logger.Debug("dropping unavailable conn") peers := make(map[peer.ID]struct{}) for _, c := range cl.h.Network().Conns() { if _, ok := peers[c.RemotePeer()]; !ok { peers[c.RemotePeer()] = struct{}{} } } unavailable := uint32(0) wg := sync.WaitGroup{} ctx, cancel := context.WithCancel(cl.rootCtx) for p := range peers { cping := cl.ps.Ping(ctx, p) wg.Add(1) go func(peer peer.ID) { defer wg.Done() select { case ret := <-cping: if ret.Error == nil { return // everything should be ok } case <-time.After(ConnLifecyclePingTimeout): } // connection should be dead atomic.AddUint32(&unavailable, 1) // if we are here, conn should be kill if err := cl.h.Network().ClosePeer(peer); err != nil { cl.logger.Warn("unable to close connection", zap.Error(err)) } }(p) } wg.Wait() cancel() if unavailable > 0 { available := uint32(len(peers)) - unavailable cl.logger.Debug("dropped unavailable peers", zap.Uint32("available", available), zap.Uint32("unavailable", unavailable)) } else { cl.logger.Debug("all peers are available") } } func (cl *ConnLifecycle) monitorPeerOfInterest(ctx context.Context) error { sub, err := cl.h.EventBus().Subscribe([]any{ new(EvtPeerTag), }, eventbus.Name("weshnet/lifecycle/monitor-peer-of-interest")) if err != nil { return fmt.Errorf("unable to subscribe to `EvtPeerConnectednessChanged`: %w", err) } for _, p := range cl.h.Peerstore().Peers() { if tag := cl.h.ConnManager().GetTagInfo(p); tag != nil && tag.Value >= ConnPeerOfInterestMinScore { infos := cl.h.Peerstore().PeerInfo(p) cl.peering.AddPeer(infos) cl.logger.Debug("adding peer of interest", logutil.PrivateStringer("peer", p), zap.Int("score", tag.Value)) } } go func() { defer sub.Close() for { var e any select { case e = <-sub.Out(): case <-ctx.Done(): return } evt := e.(EvtPeerTag) oldTotal := evt.Total - evt.Diff if evt.Total >= ConnPeerOfInterestMinScore && oldTotal < ConnPeerOfInterestMinScore { infos := cl.h.Peerstore().PeerInfo(evt.Peer) cl.peering.AddPeer(infos) cl.logger.Debug("marking peer as peer of interest", logutil.PrivateStringer("peer", evt.Peer), zap.Int("score", evt.Total), zap.Int("diff", evt.Diff), zap.String("last_tag", evt.Tag)) } else if evt.Total < ConnPeerOfInterestMinScore && oldTotal >= ConnPeerOfInterestMinScore { cl.peering.RemovePeer(evt.Peer) cl.logger.Debug("unmarking peer as peer of interest", logutil.PrivateStringer("peer", evt.Peer), zap.Int("score", evt.Total), zap.Int("diff", evt.Diff), zap.String("last_tag", evt.Tag)) } } }() return nil } ================================================ FILE: pkg/ipfsutil/localrecord.go ================================================ package ipfsutil import ( "context" "os" ipfs_core "github.com/ipfs/kubo/core" coreiface "github.com/ipfs/kubo/core/coreiface" "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/network" "github.com/libp2p/go-libp2p/core/protocol" ma "github.com/multiformats/go-multiaddr" mafmt "github.com/multiformats/go-multiaddr-fmt" manet "github.com/multiformats/go-multiaddr/net" mc "berty.tech/weshnet/v2/pkg/multipeer-connectivity-driver" ) const recProtocolID = protocol.ID("wesh/p2p/localrecord") type LocalRecord struct { host host.Host } // OptionLocalRecord is given to CoreAPIOption.Options when the ipfs node setup func OptionLocalRecord(node *ipfs_core.IpfsNode, _ coreiface.CoreAPI) error { lr := &LocalRecord{ host: node.PeerHost, } lr.host.Network().Notify(lr) lr.host.SetStreamHandler(recProtocolID, lr.handleLocalRecords) return nil } // called when network starts listening on an addr func (lr *LocalRecord) Listen(network.Network, ma.Multiaddr) {} // called when network stops listening on an addr func (lr *LocalRecord) ListenClose(network.Network, ma.Multiaddr) {} // called when a connection opened func (lr *LocalRecord) Connected(_ network.Network, c network.Conn) { ctx := 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 go func() { if manet.IsPrivateAddr(c.RemoteMultiaddr()) || mafmt.Base(mc.ProtocolCode).Matches(c.RemoteMultiaddr()) { if err := lr.sendLocalRecord(ctx, c); err != nil { return } } }() } // called when a connection closed func (lr *LocalRecord) Disconnected(network.Network, network.Conn) {} // called when a stream opened func (lr *LocalRecord) OpenedStream(network.Network, network.Stream) {} // called when a stream closed func (lr *LocalRecord) ClosedStream(network.Network, network.Stream) {} func (lr *LocalRecord) sendLocalRecord(ctx context.Context, c network.Conn) error { s, err := c.NewStream(ctx) if err != nil { return err } return s.SetProtocol(recProtocolID) } func (lr *LocalRecord) handleLocalRecords(network.Stream) { os.Stderr.WriteString("handleLocalRecords") } ================================================ FILE: pkg/ipfsutil/metrics.go ================================================ package ipfsutil ================================================ FILE: pkg/ipfsutil/mobile/host.go ================================================ package node import ( "fmt" ipfs_p2p "github.com/ipfs/kubo/core/node/libp2p" p2p "github.com/libp2p/go-libp2p" p2p_host "github.com/libp2p/go-libp2p/core/host" p2p_peer "github.com/libp2p/go-libp2p/core/peer" p2p_pstore "github.com/libp2p/go-libp2p/core/peerstore" ) // HostMobile is a p2p_host.Host var _ p2p_host.Host = (*HostMobile)(nil) type HostConfigFunc func(p2p_host.Host) error // @TODO: add custom mobile option here type HostConfig struct { // called after host init ConfigFunc HostConfigFunc // p2p options Options []p2p.Option } func ChainHostConfig(cfgs ...HostConfigFunc) HostConfigFunc { return func(host p2p_host.Host) (err error) { for _, cfg := range cfgs { if cfg == nil { continue // skip empty config } if err = cfg(host); err != nil { return } } return } } type HostMobile struct { p2p_host.Host } func NewHostConfigOption(hopt ipfs_p2p.HostOption, cfg *HostConfig) ipfs_p2p.HostOption { return func(id p2p_peer.ID, ps p2p_pstore.Peerstore, options ...p2p.Option) (p2p_host.Host, error) { // add p2p custom options if cfg.Options != nil { options = append(options, cfg.Options...) } host, err := hopt(id, ps, options...) if err != nil { return nil, err } if cfg.ConfigFunc != nil { // apply host custom config if err := cfg.ConfigFunc(host); err != nil { return nil, fmt.Errorf("unable to apply host config: %w", err) } } return host, nil } } ================================================ FILE: pkg/ipfsutil/mobile/node.go ================================================ package node import ( "context" "fmt" "net" "os" ipfs_oldcmds "github.com/ipfs/kubo/commands" ipfs_core "github.com/ipfs/kubo/core" ipfs_corehttp "github.com/ipfs/kubo/core/corehttp" ipfs_p2p "github.com/ipfs/kubo/core/node/libp2p" p2p_host "github.com/libp2p/go-libp2p/core/host" ) type IpfsConfig struct { HostConfig *HostConfig HostOption ipfs_p2p.HostOption RoutingConfig *RoutingConfig RoutingOption ipfs_p2p.RoutingOption RepoMobile *RepoMobile ExtraOpts map[string]bool } func (c *IpfsConfig) fillDefault() error { if c.RepoMobile == nil { return fmt.Errorf("repo cannot be nil") } if c.ExtraOpts == nil { c.ExtraOpts = make(map[string]bool) } if c.RoutingOption == nil { c.RoutingOption = ipfs_p2p.DHTOption } if c.RoutingConfig == nil { c.RoutingConfig = &RoutingConfig{} } if c.HostOption == nil { c.HostOption = ipfs_p2p.DefaultHostOption } if c.HostConfig == nil { c.HostConfig = &HostConfig{} } return nil } type IpfsMobile struct { *ipfs_core.IpfsNode Repo *RepoMobile commandCtx ipfs_oldcmds.Context } func (im *IpfsMobile) PeerHost() p2p_host.Host { return im.IpfsNode.PeerHost } func (im *IpfsMobile) Close() error { return im.IpfsNode.Close() } func (im *IpfsMobile) ServeCoreHTTP(l net.Listener, opts ...ipfs_corehttp.ServeOption) error { gatewayOpt := ipfs_corehttp.GatewayOption(ipfs_corehttp.WebUIPaths...) opts = append(opts, ipfs_corehttp.WebUIOption, gatewayOpt, ipfs_corehttp.CommandsOption(im.commandCtx), ) return ipfs_corehttp.Serve(im.IpfsNode, l, opts...) } func (im *IpfsMobile) ServeGateway(l net.Listener, opts ...ipfs_corehttp.ServeOption) error { opts = append(opts, ipfs_corehttp.HostnameOption(), ipfs_corehttp.GatewayOption("/ipfs", "/ipns"), ipfs_corehttp.VersionOption(), ipfs_corehttp.CheckVersionOption(), ipfs_corehttp.CommandsOption(im.commandCtx), ) return ipfs_corehttp.Serve(im.IpfsNode, l, opts...) } func NewNode(ctx context.Context, cfg *IpfsConfig) (*IpfsMobile, error) { if err := cfg.fillDefault(); err != nil { return nil, fmt.Errorf("invalid configuration: %w", err) } // build config buildcfg := &ipfs_core.BuildCfg{ Online: true, Permanent: false, DisableEncryptedConnections: false, Repo: cfg.RepoMobile, Host: NewHostConfigOption(cfg.HostOption, cfg.HostConfig), Routing: NewRoutingConfigOption(cfg.RoutingOption, cfg.RoutingConfig), ExtraOpts: cfg.ExtraOpts, } os.Setenv("IPFS_PATH", cfg.RepoMobile.Path) // create ipfs node inode, err := ipfs_core.NewNode(ctx, buildcfg) if err != nil { // unlockRepo(repoPath) return nil, fmt.Errorf("failed to init ipfs node: %s", err) } // @TODO: no sure about how to init this, must be another way cctx := ipfs_oldcmds.Context{ ConfigRoot: cfg.RepoMobile.Path, ReqLog: &ipfs_oldcmds.ReqLog{}, ConstructNode: func() (*ipfs_core.IpfsNode, error) { return inode, nil }, } return &IpfsMobile{ commandCtx: cctx, IpfsNode: inode, Repo: cfg.RepoMobile, }, nil } ================================================ FILE: pkg/ipfsutil/mobile/repo.go ================================================ package node import ( ipfs_config "github.com/ipfs/kubo/config" ipfs_repo "github.com/ipfs/kubo/repo" ) var _ ipfs_repo.Repo = (*RepoMobile)(nil) type RepoConfigPatch func(cfg *ipfs_config.Config) (err error) type RepoMobile struct { ipfs_repo.Repo Path string } func NewRepoMobile(path string, repo ipfs_repo.Repo) *RepoMobile { return &RepoMobile{ Repo: repo, Path: path, } } func (mr *RepoMobile) ApplyPatchs(patchs ...RepoConfigPatch) error { cfg, err := mr.Config() if err != nil { return err } if err := ChainIpfsConfigPatch(patchs...)(cfg); err != nil { return err } return mr.SetConfig(cfg) } func ChainIpfsConfigPatch(patchs ...RepoConfigPatch) RepoConfigPatch { return func(cfg *ipfs_config.Config) (err error) { for _, patch := range patchs { if patch == nil { continue // skip empty patch } if err = patch(cfg); err != nil { return } } return } } ================================================ FILE: pkg/ipfsutil/mobile/routing.go ================================================ package node import ( "fmt" ipfs_p2p "github.com/ipfs/kubo/core/node/libp2p" p2p_host "github.com/libp2p/go-libp2p/core/host" p2p_routing "github.com/libp2p/go-libp2p/core/routing" ) type RoutingConfigFunc func(p2p_host.Host, p2p_routing.Routing) error type RoutingConfig struct { ConfigFunc RoutingConfigFunc } func NewRoutingConfigOption(ro ipfs_p2p.RoutingOption, rc *RoutingConfig) ipfs_p2p.RoutingOption { return func(args ipfs_p2p.RoutingOptionArgs) (p2p_routing.Routing, error) { routing, err := ro(args) if err != nil { return nil, err } if rc.ConfigFunc != nil { if err := rc.ConfigFunc(args.Host, routing); err != nil { return nil, fmt.Errorf("failed to config routing: %w", err) } } return routing, nil } } ================================================ FILE: pkg/ipfsutil/mobile.go ================================================ package ipfsutil import ( "context" "fmt" ipfs_config "github.com/ipfs/kubo/config" ipfs_p2p "github.com/ipfs/kubo/core/node/libp2p" p2p "github.com/libp2p/go-libp2p" dht "github.com/libp2p/go-libp2p-kad-dht" "github.com/libp2p/go-libp2p-kad-dht/dual" host "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/peer" p2p_routing "github.com/libp2p/go-libp2p/core/routing" quict "github.com/libp2p/go-libp2p/p2p/transport/quic" "github.com/libp2p/go-libp2p/p2p/transport/quicreuse" tcpt "github.com/libp2p/go-libp2p/p2p/transport/tcp" "go.uber.org/zap" ipfs_mobile "berty.tech/weshnet/v2/pkg/ipfsutil/mobile" ) type DHTNetworkMode int const ( DHTNetworkLan DHTNetworkMode = iota DHTNetworkWan DHTNetworkDual ) type Config func(cfg *ipfs_config.Config) ([]p2p.Option, error) type MobileOptions struct { Logger *zap.Logger IpfsConfigPatch Config // P2PStaticRelays and PeerStorePeers are only used if IpfsConfigPatch is nil P2PStaticRelays []string PeerStorePeers []string HostOption ipfs_p2p.HostOption RoutingOption ipfs_p2p.RoutingOption HostConfigFunc ipfs_mobile.HostConfigFunc RoutingConfigFunc ipfs_mobile.RoutingConfigFunc ExtraOpts map[string]bool } func (o *MobileOptions) fillDefault() { if o.Logger == nil { o.Logger = zap.NewNop() } if o.HostOption == nil { o.HostOption = ipfs_p2p.DefaultHostOption } if o.RoutingOption == nil { o.RoutingOption = CustomRoutingOption(dht.ModeClient, DHTNetworkDual, dht.Concurrency(2)) } if o.IpfsConfigPatch == nil { o.IpfsConfigPatch = o.defaultIpfsConfigPatch // P2PStaticRelays and PeerStorePeers are only used by defaultIpfsConfigPatch if o.P2PStaticRelays == nil { o.P2PStaticRelays = []string{DefaultP2PStaticRelay} } if o.PeerStorePeers == nil { o.PeerStorePeers = []string{DefaultP2PRdvpMaddr} } } // apply default extras if o.ExtraOpts == nil { o.ExtraOpts = make(map[string]bool) } // if not set, disable pubsub by default to avoid collision if _, ok := o.ExtraOpts["pubsub"]; !ok { o.ExtraOpts["pubsub"] = false } } func NewIPFSMobile(ctx context.Context, repo *ipfs_mobile.RepoMobile, opts *MobileOptions) (*ipfs_mobile.IpfsMobile, error) { opts.fillDefault() var p2popts []p2p.Option err := repo.ApplyPatchs(func(cfg *ipfs_config.Config) error { var err error if p2popts, err = opts.IpfsConfigPatch(cfg); err != nil { return err } return nil }) if err != nil { return nil, err } // check that p2p opt is set if p2popts == nil { return nil, fmt.Errorf("unable p2p option: cannot be nil") } // configure host hostconfig := &ipfs_mobile.HostConfig{ // called after host init ConfigFunc: opts.HostConfigFunc, // p2p options Options: p2popts, } // configure routing routingconfig := &ipfs_mobile.RoutingConfig{ // called after host init ConfigFunc: opts.RoutingConfigFunc, } // configure ipfs mobile ipfsconfig := ipfs_mobile.IpfsConfig{ HostConfig: hostconfig, RoutingConfig: routingconfig, RepoMobile: repo, ExtraOpts: opts.ExtraOpts, HostOption: opts.HostOption, RoutingOption: opts.RoutingOption, } return ipfs_mobile.NewNode(ctx, &ipfsconfig) } func CustomRoutingOption(mode dht.ModeOpt, net DHTNetworkMode, opts ...dht.Option) func(args ipfs_p2p.RoutingOptionArgs) (p2p_routing.Routing, error) { return func(args ipfs_p2p.RoutingOptionArgs) (p2p_routing.Routing, error) { opts = append(opts, dht.Mode(mode), dht.Datastore(args.Datastore), dht.Validator(args.Validator), dht.BootstrapPeers(args.BootstrapPeers...), ) return newDualDHT(args.Ctx, args.Host, net, opts...) } } func (o *MobileOptions) defaultIpfsConfigPatch(cfg *ipfs_config.Config) ([]p2p.Option, error) { // Imitate berty setupIPFSConfig // https://github.com/berty/berty/blob/5a8b9cb8524c1287ab2533a9e186ac8bde7f2b57/go/internal/initutil/ipfs.go#L474C19-L474C34 p2popts := []p2p.Option{} // make sure relay is enabled cfg.Swarm.RelayClient.Enabled = ipfs_config.True cfg.Swarm.Transports.Network.Relay = ipfs_config.True // add static relay pis, err := ParseAndResolveMaddrs(context.TODO(), o.Logger, o.P2PStaticRelays) if err != nil { return nil, err } if len(pis) > 0 { peers := make([]peer.AddrInfo, len(pis)) for i, p := range pis { peers[i] = *p } p2popts = append(p2popts, p2p.EnableAutoRelayWithStaticRelays(peers)) } // prefill peerstore with known rdvp servers peers, err := ParseAndResolveMaddrs(context.TODO(), o.Logger, o.PeerStorePeers) if err != nil { return nil, err } for _, p := range peers { cfg.Peering.Peers = append(cfg.Peering.Peers, *p) } // @NOTE(gfanton): disable quic transport so we can init a custom transport // with reusport disabled cfg.Swarm.Transports.Network.QUIC = ipfs_config.False p2popts = append(p2popts, p2p.Transport(quict.NewTransport), p2p.QUICReuse(quicreuse.NewConnManager, quicreuse.DisableReuseport())) // @NOTE(gfanton): disable tcp transport so we can init a custom transport // with reusport disabled cfg.Swarm.Transports.Network.TCP = ipfs_config.False p2popts = append(p2popts, p2p.Transport(tcpt.NewTCPTransport, tcpt.DisableReuseport(), )) return p2popts, nil } const ( // from dual package dht maxPrefixCountPerCpl = 2 maxPrefixCount = 3 ) func newDualDHT(ctx context.Context, h host.Host, net DHTNetworkMode, options ...dht.Option) (p2p_routing.Routing, error) { switch net { case DHTNetworkWan: options = append(options, dht.QueryFilter(dht.PublicQueryFilter), dht.RoutingTableFilter(dht.PublicRoutingTableFilter), dht.RoutingTablePeerDiversityFilter(dht.NewRTPeerDiversityFilter(h, maxPrefixCountPerCpl, maxPrefixCount)), ) return dht.New(ctx, h, options...) case DHTNetworkLan: options = append(options, dht.ProtocolExtension(dual.LanExtension), dht.QueryFilter(dht.PrivateQueryFilter), dht.RoutingTableFilter(dht.PrivateRoutingTableFilter), ) return dht.New(ctx, h, options...) default: // dual return dual.New(ctx, h, dual.DHTOption(options...)) } } ================================================ FILE: pkg/ipfsutil/peering.go ================================================ // mostly from: https://github.com/ipfs/kubo/blob/master/peering/peering.go package ipfsutil import ( "context" "errors" "sync" "time" "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/network" "github.com/libp2p/go-libp2p/core/peer" backoff "github.com/libp2p/go-libp2p/p2p/discovery/backoff" "github.com/multiformats/go-multiaddr" "go.uber.org/zap" "berty.tech/weshnet/v2/pkg/logutil" ) // Seed the random number generator. // // We don't need good randomness, but we do need randomness. const ( connmgrTag = "berty-peering" // MaximumReconnectingDelay define the maximum time a peer is able to // reconnect, if reached peer will be remove from the store MaximumReconnectingDelay = time.Second * 30 ) type state int const ( stateInit state = iota stateRunning stateStopped ) // PeeringService maintains connections to specified peers, reconnecting on // disconnect with a back-off. type PeeringService struct { host host.Host logger *zap.Logger backoffFactory backoff.BackoffFactory muPeers sync.RWMutex peers map[peer.ID]*peerHandler state state } // NewPeeringService constructs a new peering service. Peers can be added and // removed immediately, but connections won't be formed until `Start` is called. func NewPeeringService(logger *zap.Logger, host host.Host, fact backoff.BackoffFactory) *PeeringService { return &PeeringService{ backoffFactory: fact, logger: logger, host: host, peers: make(map[peer.ID]*peerHandler), } } // Start starts the peering service, connecting and maintaining connections to // all registered peers. It returns an error if the service has already been // stopped. func (ps *PeeringService) Start() error { ps.muPeers.Lock() defer ps.muPeers.Unlock() switch ps.state { case stateInit: ps.logger.Info("starting peering service") case stateRunning: return nil case stateStopped: return errors.New("peering service already stopped") } ps.host.Network().Notify((*netNotifee)(ps)) ps.state = stateRunning for _, handler := range ps.peers { go handler.startIfDisconnected() } return nil } // Stop stops the peering service. func (ps *PeeringService) Stop() error { ps.host.Network().StopNotify((*netNotifee)(ps)) ps.muPeers.Lock() defer ps.muPeers.Unlock() switch ps.state { case stateInit, stateRunning: ps.logger.Info("stopping peering service") for _, handler := range ps.peers { handler.stop() } ps.state = stateStopped } return nil } // AddPeer adds a peer to the peering service. This function may be safely // called at any time: before the service is started, while running, or after it // stops. // // Add peer may also be called multiple times for the same peer. The new // addresses will replace the old. func (ps *PeeringService) AddPeer(info peer.AddrInfo) { ps.muPeers.Lock() handler, ok := ps.peers[info.ID] ps.muPeers.Unlock() if ok { ps.logger.Info("updating addresses", logutil.PrivateStringer("peer", info.ID), zap.Any("addrs", info.Addrs)) handler.setAddrs(info.Addrs) } else { ps.logger.Info("peer added", logutil.PrivateStringer("peer", info.ID), zap.Any("addrs", info.Addrs)) ps.host.ConnManager().Protect(info.ID, connmgrTag) handler = &peerHandler{ logger: ps.logger, host: ps.host, peer: info.ID, addrs: info.Addrs, backoffStrat: ps.backoffFactory(), } handler.ctx, handler.cancel = context.WithCancel(context.Background()) ps.peers[info.ID] = handler switch ps.state { case stateRunning: go handler.startIfDisconnected() case stateStopped: // We still construct everything in this state because // it's easier to reason about. But we should still free // resources. handler.cancel() } } } // ListPeers lists peers in the peering service. func (ps *PeeringService) ListPeers() []peer.AddrInfo { ps.muPeers.RLock() defer ps.muPeers.RUnlock() out := make([]peer.AddrInfo, 0, len(ps.peers)) for id, addrs := range ps.peers { ai := peer.AddrInfo{ID: id} ai.Addrs = append(ai.Addrs, addrs.addrs...) out = append(out, ai) } return out } // RemovePeer removes a peer from the peering service. This function may be // safely called at any time: before the service is started, while running, or // after it stops. func (ps *PeeringService) RemovePeer(id peer.ID) { ps.muPeers.Lock() defer ps.muPeers.Unlock() if handler, ok := ps.peers[id]; ok { ps.logger.Info("peer removed", logutil.PrivateStringer("peer", id)) ps.host.ConnManager().Unprotect(id, connmgrTag) handler.stop() delete(ps.peers, id) } } type netNotifee PeeringService func (nn *netNotifee) Connected(_ network.Network, c network.Conn) { ps := (*PeeringService)(nn) p := c.RemotePeer() ps.muPeers.RLock() defer ps.muPeers.RUnlock() if handler, ok := ps.peers[p]; ok { // use a goroutine to avoid blocking events. go handler.stopIfConnected() } } func (nn *netNotifee) Disconnected(_ network.Network, c network.Conn) { ps := (*PeeringService)(nn) p := c.RemotePeer() ps.muPeers.RLock() defer ps.muPeers.RUnlock() if handler, ok := ps.peers[p]; ok { // use a goroutine to avoid blocking events. go handler.startIfDisconnected() } } func (nn *netNotifee) OpenedStream(network.Network, network.Stream) {} func (nn *netNotifee) ClosedStream(network.Network, network.Stream) {} func (nn *netNotifee) Listen(network.Network, multiaddr.Multiaddr) {} func (nn *netNotifee) ListenClose(network.Network, multiaddr.Multiaddr) {} // peerHandler keeps track of all state related to a specific "peering" peer. type peerHandler struct { peer peer.ID host host.Host ctx context.Context cancel context.CancelFunc logger *zap.Logger addrs []multiaddr.Multiaddr backoffStrat backoff.BackoffStrategy reconnectTimer *time.Timer muHandler sync.Mutex } // setAddrs sets the addresses for this peer. func (ph *peerHandler) setAddrs(addrs []multiaddr.Multiaddr) { // Not strictly necessary, but it helps to not trust the calling code. addrCopy := make([]multiaddr.Multiaddr, len(addrs)) copy(addrCopy, addrs) ph.muHandler.Lock() ph.addrs = addrCopy ph.muHandler.Unlock() } // getAddrs returns a shared slice of addresses for this peer. Do not modify. func (ph *peerHandler) getAddrs() []multiaddr.Multiaddr { ph.muHandler.Lock() defer ph.muHandler.Unlock() return ph.addrs } // stop permanently stops the peer handler. func (ph *peerHandler) stop() { ph.cancel() ph.muHandler.Lock() if ph.reconnectTimer != nil { ph.reconnectTimer.Stop() ph.reconnectTimer = nil } ph.muHandler.Unlock() } func (ph *peerHandler) nextBackoff() time.Duration { return ph.backoffStrat.Delay() } func (ph *peerHandler) reconnect() { // Try connecting addrs := ph.getAddrs() ph.logger.Debug("reconnecting", logutil.PrivateStringer("peer", ph.peer), zap.Any("addrs", addrs)) err := ph.host.Connect(ph.ctx, peer.AddrInfo{ID: ph.peer, Addrs: addrs}) if err != nil { delay := ph.nextBackoff() if delay > MaximumReconnectingDelay { ph.logger.Debug("peer unavailable", logutil.PrivateStringer("peer", ph.peer)) ph.stop() return } ph.logger.Debug("failed to reconnect", zap.Duration("next_try", delay), logutil.PrivateStringer("peer", ph.peer), zap.Error(err)) // Ok, we failed. Extend the timeout. ph.muHandler.Lock() if ph.reconnectTimer != nil { // Only counts if the reconnectTimer still exists. If not, a // connection _was_ somehow established. ph.reconnectTimer.Reset(delay) } // Otherwise, someone else has stopped us so we can assume that // we're either connected or someone else will start us. ph.muHandler.Unlock() } // Always call this. We could have connected since we processed the // error. ph.stopIfConnected() } func (ph *peerHandler) stopIfConnected() { ph.muHandler.Lock() defer ph.muHandler.Unlock() if ph.reconnectTimer != nil && ph.host.Network().Connectedness(ph.peer) == network.Connected { ph.logger.Debug("successfully reconnected", logutil.PrivateStringer("peer", ph.peer)) // stop reconnect timer ph.reconnectTimer.Stop() ph.reconnectTimer = nil } } // startIfDisconnected is the inverse of stopIfConnected. func (ph *peerHandler) startIfDisconnected() { ph.muHandler.Lock() defer ph.muHandler.Unlock() if ph.reconnectTimer == nil && ph.host.Network().Connectedness(ph.peer) != network.Connected { // reset backoff ph.backoffStrat.Reset() delay := ph.nextBackoff() ph.logger.Debug("disconnected from peer, waiting for reconnection", logutil.PrivateStringer("peer", ph.peer), zap.Duration("delay", delay)) // Always start with a short timeout so we can stagger things a bit. ph.reconnectTimer = time.AfterFunc(delay, ph.reconnect) } } ================================================ FILE: pkg/ipfsutil/pubsub_adaptater.go ================================================ package ipfsutil import ( coreiface "github.com/ipfs/kubo/core/coreiface" ) type pubsubCoreAPIAdapter struct { coreiface.PubSubAPI coreiface.CoreAPI } func (ps *pubsubCoreAPIAdapter) PubSub() coreiface.PubSubAPI { return ps.PubSubAPI } func InjectPubSubAPI(api coreiface.CoreAPI, ps coreiface.PubSubAPI) coreiface.CoreAPI { return &pubsubCoreAPIAdapter{ PubSubAPI: ps, CoreAPI: api, } } type pubsubExtendedCoreAPIAdapter struct { coreiface.PubSubAPI ExtendedCoreAPI } func (ps *pubsubExtendedCoreAPIAdapter) PubSub() coreiface.PubSubAPI { return ps.PubSubAPI } func InjectPubSubCoreAPIExtendedAdapter(exapi ExtendedCoreAPI, ps coreiface.PubSubAPI) ExtendedCoreAPI { return &pubsubExtendedCoreAPIAdapter{ PubSubAPI: ps, ExtendedCoreAPI: exapi, } } ================================================ FILE: pkg/ipfsutil/pubsub_api.go ================================================ package ipfsutil import ( "context" "sync" coreiface "github.com/ipfs/kubo/core/coreiface" coreiface_options "github.com/ipfs/kubo/core/coreiface/options" p2p_pubsub "github.com/libp2p/go-libp2p-pubsub" p2p_peer "github.com/libp2p/go-libp2p/core/peer" "go.uber.org/zap" "berty.tech/weshnet/v2/pkg/logutil" ) type PubSubAPI struct { *p2p_pubsub.PubSub logger *zap.Logger muTopics sync.RWMutex topics map[string]*p2p_pubsub.Topic } func NewPubSubAPI(_ context.Context, logger *zap.Logger, ps *p2p_pubsub.PubSub) coreiface.PubSubAPI { return &PubSubAPI{ PubSub: ps, logger: logger, topics: make(map[string]*p2p_pubsub.Topic), } } func (ps *PubSubAPI) topicJoin(topic string, opts ...p2p_pubsub.TopicOpt) (*p2p_pubsub.Topic, error) { ps.muTopics.Lock() defer ps.muTopics.Unlock() var err error t, ok := ps.topics[topic] if ok { return t, nil } if t, err = ps.PubSub.Join(topic, opts...); err != nil { return nil, err } if _, err = t.Relay(); err != nil { t.Close() return nil, err } ps.topics[topic] = t return t, nil } // func (ps *PubSubAPI) topicLeave(topic string) (err error) { // ps.muTopics.Lock() // if t, ok := ps.topics[topic]; ok { // err = t.Close() // delete(ps.topics, topic) // } // ps.muTopics.Unlock() // return // } // Ls lists subscribed topics by name func (ps *PubSubAPI) Ls(context.Context) ([]string, error) { return ps.PubSub.GetTopics(), nil } // Peers list peers we are currently pubsubbing with func (ps *PubSubAPI) Peers(_ context.Context, opts ...coreiface_options.PubSubPeersOption) ([]p2p_peer.ID, error) { s, err := coreiface_options.PubSubPeersOptions(opts...) if err != nil { return nil, err } return ps.PubSub.ListPeers(s.Topic), nil } var minTopicSize = p2p_pubsub.WithReadiness(p2p_pubsub.MinTopicSize(1)) // Publish a message to a given pubsub topic func (ps *PubSubAPI) Publish(ctx context.Context, topic string, msg []byte) error { t, err := ps.topicJoin(topic) if err != nil { return err } return t.Publish(ctx, msg, minTopicSize) } // Subscribe to messages on a given topic func (ps *PubSubAPI) Subscribe(_ context.Context, topic string, _ ...coreiface_options.PubSubSubscribeOption) (coreiface.PubSubSubscription, error) { t, err := ps.topicJoin(topic) if err != nil { return nil, err } ps.logger.Debug("subscribing", logutil.PrivateString("topic", topic)) sub, err := t.Subscribe() if err != nil { return nil, err } return &pubsubSubscriptionAPI{ps.logger, sub}, nil } // PubSubSubscription is an active PubSub subscription type pubsubSubscriptionAPI struct { logger *zap.Logger *p2p_pubsub.Subscription } // io.Closer func (pss *pubsubSubscriptionAPI) Close() (_ error) { pss.Subscription.Cancel() return } // Next return the next incoming message func (pss *pubsubSubscriptionAPI) Next(ctx context.Context) (coreiface.PubSubMessage, error) { m, err := pss.Subscription.Next(ctx) if err != nil { return nil, err } return &pubsubMessageAPI{m}, nil } // PubSubMessage is a single PubSub message type pubsubMessageAPI struct { *p2p_pubsub.Message } // From returns id of a peer from which the message has arrived func (psm *pubsubMessageAPI) From() p2p_peer.ID { return psm.Message.GetFrom() } // Data returns the message body func (psm *pubsubMessageAPI) Data() []byte { return psm.Message.GetData() } // Seq returns message identifier func (psm *pubsubMessageAPI) Seq() []byte { return psm.Message.GetSeqno() } // // Topics returns list of topics this message was set to func (psm *pubsubMessageAPI) Topics() []string { if psm.Message.Topic == nil { return nil } return []string{*psm.Message.Topic} } ================================================ FILE: pkg/ipfsutil/pubsub_monitor.go ================================================ package ipfsutil import ( "sync" ps "github.com/libp2p/go-libp2p-pubsub" ps_pb "github.com/libp2p/go-libp2p-pubsub/pb" "github.com/libp2p/go-libp2p/core/event" host "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/peer" "go.uber.org/zap" "berty.tech/weshnet/v2/pkg/logutil" ) // PubsubMonitor is an EventTracer var _ ps.EventTracer = (*PubsubMonitor)(nil) type EventMonitor int const ( TypeEventMonitorPeerUnknown EventMonitor = iota TypeEventMonitorPeerJoined TypeEventMonitorPeerLeft ) type EventTracer interface { EventTracerOption() ps.Option } type EvtPubSubTopic struct { EventType EventMonitor Topic string PeerID peer.ID } type PubsubMonitor struct { logger *zap.Logger h host.Host ps *ps.PubSub emitter event.Emitter muPeersTopics sync.Mutex peersTopics map[peer.ID][]string } func NewPubsubMonitor(l *zap.Logger, h host.Host) (EventTracer, error) { emitter, err := h.EventBus().Emitter(new(EvtPubSubTopic)) if err != nil { return nil, err } return &PubsubMonitor{ h: h, logger: l, emitter: emitter, peersTopics: make(map[peer.ID][]string), }, nil } func (pt *PubsubMonitor) EventTracerOption() ps.Option { return func(p *ps.PubSub) error { pt.ps = p return ps.WithEventTracer(pt)(p) } } func (pt *PubsubMonitor) Trace(e *ps_pb.TraceEvent) { switch e.GetType() { case ps_pb.TraceEvent_JOIN: topic := e.GetJoin().GetTopic() peer := pt.h.ID() pt.Emit(&EvtPubSubTopic{ EventType: TypeEventMonitorPeerJoined, Topic: topic, PeerID: peer, }) case ps_pb.TraceEvent_LEAVE: topic := e.GetLeave().GetTopic() peer := pt.h.ID() pt.Emit(&EvtPubSubTopic{ EventType: TypeEventMonitorPeerLeft, Topic: topic, PeerID: peer, }) case ps_pb.TraceEvent_REMOVE_PEER: peerid, err := peer.IDFromBytes(e.GetRemovePeer().GetPeerID()) if err != nil { pt.logger.Warn("unable to parse peerid", zap.String("type", e.GetType().String()), logutil.PrivateString("topic", e.GetGraft().GetTopic())) return } topics := pt.popTopicFromPeer(peerid) for _, topic := range topics { pt.Emit(&EvtPubSubTopic{ EventType: TypeEventMonitorPeerLeft, Topic: topic, PeerID: peerid, }) } case ps_pb.TraceEvent_GRAFT: topic := e.GetGraft().GetTopic() peerid, err := peer.IDFromBytes(e.GetGraft().GetPeerID()) if err != nil { pt.logger.Warn("unable to parse peerid", zap.String("type", e.GetType().String()), logutil.PrivateString("topic", e.GetGraft().GetTopic())) return } pt.addTopicToPeer(peerid, topic) pt.Emit(&EvtPubSubTopic{ EventType: TypeEventMonitorPeerJoined, Topic: topic, PeerID: peerid, }) case ps_pb.TraceEvent_PRUNE: // @FIXME(gfanton): send this info as well } } func (pt *PubsubMonitor) Emit(e *EvtPubSubTopic) { if err := pt.emitter.Emit(*e); err != nil { pt.logger.Warn("unable to emit pubsub event") } } func (pt *PubsubMonitor) popTopicFromPeer(p peer.ID) []string { pt.muPeersTopics.Lock() defer pt.muPeersTopics.Unlock() if topics, ok := pt.peersTopics[p]; ok { delete(pt.peersTopics, p) return topics } return []string{} } func (pt *PubsubMonitor) addTopicToPeer(p peer.ID, ns string) { pt.muPeersTopics.Lock() topics, ok := pt.peersTopics[p] if !ok { topics = make([]string, 0) } pt.peersTopics[p] = append(topics, ns) pt.muPeersTopics.Unlock() } ================================================ FILE: pkg/ipfsutil/repo.go ================================================ package ipfsutil import ( crand "crypto/rand" "encoding/base64" "fmt" "path/filepath" "time" ipfs_ds "github.com/ipfs/go-datastore" ipfs_cfg "github.com/ipfs/kubo/config" ipfs_loader "github.com/ipfs/kubo/plugin/loader" ipfs_repo "github.com/ipfs/kubo/repo" ipfs_fsrepo "github.com/ipfs/kubo/repo/fsrepo" p2p_ci "github.com/libp2p/go-libp2p/core/crypto" p2p_peer "github.com/libp2p/go-libp2p/core/peer" "github.com/pkg/errors" "berty.tech/weshnet/v2/pkg/errcode" ) // defaultConnMgrHighWater is the default value for the connection managers // 'high water' mark const defaultConnMgrHighWater = 200 // defaultConnMgrLowWater is the default value for the connection managers 'low // water' mark const defaultConnMgrLowWater = 150 // defaultConnMgrGracePeriod is the default value for the connection managers // grace period const defaultConnMgrGracePeriod = time.Second * 20 // @NOTE(gfanton): this will be removed with gomobile-ipfs var plugins *ipfs_loader.PluginLoader func CreateMockedRepo(dstore ipfs_ds.Batching) (ipfs_repo.Repo, error) { c, err := CreateBaseConfig() if err != nil { return nil, err } return &ipfs_repo.Mock{ D: dstore, C: *c, }, nil } func CreateOrLoadMockedRepo(dstore ipfs_ds.Batching) (ipfs_repo.Repo, error) { c, err := CreateBaseConfig() if err != nil { return nil, err } return &ipfs_repo.Mock{ D: dstore, C: *c, }, nil } func LoadRepoFromPath(path string) (ipfs_repo.Repo, error) { dir, _ := filepath.Split(path) if _, err := LoadPlugins(dir); err != nil { return nil, errors.Wrap(err, "failed to load plugins") } if !ipfs_fsrepo.IsInitialized(path) { cfg, err := CreateBaseConfig() if err != nil { return nil, fmt.Errorf("failed to create base config: %w", err) } ucfg, err := upgradeToPersistentConfig(cfg) if err != nil { return nil, errors.Wrap(err, "failed to upgrade repo") } if err := ipfs_fsrepo.Init(path, ucfg); err != nil { return nil, fmt.Errorf("failed to init ipfs repo: %w", err) } } return ipfs_fsrepo.Open(path) } var DefaultSwarmListeners = []string{ "/ip4/0.0.0.0/udp/0/quic-v1", "/ip6/::/udp/0/quic-v1", // "/ip4/0.0.0.0/tcp/0", // "/ip6/::/tcp/0", } func CreateBaseConfig() (*ipfs_cfg.Config, error) { c := ipfs_cfg.Config{} // set default bootstrap c.Bootstrap = ipfs_cfg.DefaultBootstrapAddresses c.Peering.Peers = []p2p_peer.AddrInfo{} // Identity if err := ResetRepoIdentity(&c); err != nil { return nil, errcode.ErrCode_TODO.Wrap(err) } // Discovery c.Discovery.MDNS.Enabled = true // swarm listeners c.Addresses.Swarm = DefaultSwarmListeners // Swarm c.Swarm.RelayClient.Enabled = ipfs_cfg.True c.Swarm.ConnMgr = ipfs_cfg.ConnMgr{ LowWater: ipfs_cfg.NewOptionalInteger(defaultConnMgrLowWater), HighWater: ipfs_cfg.NewOptionalInteger(defaultConnMgrHighWater), GracePeriod: ipfs_cfg.NewOptionalDuration(defaultConnMgrGracePeriod), Type: ipfs_cfg.NewOptionalString("basic"), } c.Routing = ipfs_cfg.Routing{ Type: ipfs_cfg.NewOptionalString("dhtclient"), } return &c, nil } func ResetRepoIdentity(c *ipfs_cfg.Config) error { priv, pub, err := p2p_ci.GenerateKeyPairWithReader(p2p_ci.Ed25519, 2048, crand.Reader) // nolint:staticcheck if err != nil { return errcode.ErrCode_TODO.Wrap(err) } pid, err := p2p_peer.IDFromPublicKey(pub) // nolint:staticcheck if err != nil { return errcode.ErrCode_TODO.Wrap(err) } privkeyb, err := p2p_ci.MarshalPrivateKey(priv) if err != nil { return errcode.ErrCode_TODO.Wrap(err) } // Identity c.Identity.PeerID = pid.String() c.Identity.PrivKey = base64.StdEncoding.EncodeToString(privkeyb) return nil } func upgradeToPersistentConfig(cfg *ipfs_cfg.Config) (*ipfs_cfg.Config, error) { cfgCopy, err := cfg.Clone() if err != nil { return nil, err } // setup the node mount points. cfgCopy.Mounts = ipfs_cfg.Mounts{ IPFS: "/ipfs", IPNS: "/ipns", } cfgCopy.Ipns = ipfs_cfg.Ipns{ ResolveCacheSize: 128, } cfgCopy.Reprovider = ipfs_cfg.Reprovider{ Interval: ipfs_cfg.NewOptionalDuration(time.Hour * 12), Strategy: ipfs_cfg.NewOptionalString("all"), } cfgCopy.Datastore = ipfs_cfg.Datastore{ StorageMax: "10GB", StorageGCWatermark: 90, // 90% GCPeriod: "1h", BloomFilterSize: 0, Spec: map[string]any{ "type": "mount", "mounts": []any{ map[string]any{ "mountpoint": "/blocks", "type": "measure", "prefix": "flatfs.datastore", "child": map[string]any{ "type": "flatfs", "path": "blocks", "sync": true, "shardFunc": "/repo/flatfs/shard/v1/next-to-last/2", }, }, map[string]any{ "mountpoint": "/", "type": "measure", "prefix": "leveldb.datastore", "child": map[string]any{ "type": "levelds", "path": "datastore", "compression": "none", }, }, }, }, } return cfgCopy, nil } func ResetExistingRepoIdentity(repo ipfs_repo.Repo) (ipfs_repo.Repo, error) { cfg, err := repo.Config() if err != nil { _ = repo.Close() return nil, errcode.ErrCode_ErrInternal.Wrap(err) } if err := ResetRepoIdentity(cfg); err != nil { return nil, errcode.ErrCode_TODO.Wrap(err) } updatedCfg, err := upgradeToPersistentConfig(cfg) if err != nil { return nil, errors.Wrap(err, "failed to upgrade repo") } err = repo.SetConfig(updatedCfg) if err != nil { return nil, errcode.ErrCode_ErrInternal.Wrap(err) } return repo, nil } func LoadPlugins(repoPath string) (*ipfs_loader.PluginLoader, error) { // nolint:unparam if plugins != nil { return plugins, nil } pluginpath := filepath.Join(repoPath, "plugins") lp, err := ipfs_loader.NewPluginLoader(pluginpath) if err != nil { return nil, err } if err = lp.Initialize(); err != nil { return nil, err } if err = lp.Inject(); err != nil { return nil, err } plugins = lp return lp, nil } ================================================ FILE: pkg/ipfsutil/testing.go ================================================ package ipfsutil import ( "context" crand "crypto/rand" "encoding/base64" "fmt" "net" "testing" rendezvous "github.com/berty/go-libp2p-rendezvous" p2p_rpdb "github.com/berty/go-libp2p-rendezvous/db/sqlcipher" ds "github.com/ipfs/go-datastore" dsync "github.com/ipfs/go-datastore/sync" ipfs_cfg "github.com/ipfs/kubo/config" ipfs_core "github.com/ipfs/kubo/core" ipfs_p2p "github.com/ipfs/kubo/core/node/libp2p" ipfs_repo "github.com/ipfs/kubo/repo" "github.com/libp2p/go-libp2p" pubsub "github.com/libp2p/go-libp2p-pubsub" p2p_ci "github.com/libp2p/go-libp2p/core/crypto" host "github.com/libp2p/go-libp2p/core/host" p2pnetwork "github.com/libp2p/go-libp2p/core/network" p2p_peer "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/peerstore" "github.com/libp2p/go-libp2p/core/protocol" mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" ma "github.com/multiformats/go-multiaddr" "github.com/stretchr/testify/require" "go.uber.org/zap" ipfs_mobile "berty.tech/weshnet/v2/pkg/ipfsutil/mobile" tinder "berty.tech/weshnet/v2/pkg/tinder" ) // CoreAPIMock implements ipfs.CoreAPI and adds some debugging helpers type CoreAPIMock interface { API() ExtendedCoreAPI PubSub() *pubsub.PubSub Tinder() *tinder.Service MockNetwork() mocknet.Mocknet MockNode() *ipfs_core.IpfsNode Close() } func getOrCreatePrivateKeyFromDatastore(t testing.TB, ctx context.Context, datastore ds.Datastore) p2p_ci.PrivKey { const datastoreKeyForPrivateKey = "p2p_private_key" privkeyb, err := datastore.Get(ctx, ds.NewKey("private_key")) if err == ds.ErrNotFound { priv, _, err := p2p_ci.GenerateKeyPairWithReader(p2p_ci.RSA, 2048, crand.Reader) if err != nil { t.Fatalf("failed to generate pair key: %v", err) } privkeyb, err := p2p_ci.MarshalPrivateKey(priv) if err != nil { t.Fatalf("failed to get raw priv key: %v", err) } if err := datastore.Put(ctx, ds.NewKey(datastoreKeyForPrivateKey), privkeyb); err != nil { t.Fatalf("failed to save priv key: %v", err) } return priv } else if err != nil { t.Fatalf("failed to get value from datastore: %v", err) } priv, err := p2p_ci.UnmarshalPrivateKey(privkeyb) if err != nil { t.Fatalf("failed to unmarshal priv key: %v", err) } return priv } func TestingRepo(t testing.TB, ctx context.Context, datastore ds.Datastore) ipfs_repo.Repo { t.Helper() c := ipfs_cfg.Config{} priv := getOrCreatePrivateKeyFromDatastore(t, ctx, datastore) pid, err := p2p_peer.IDFromPublicKey(priv.GetPublic()) if err != nil { t.Fatalf("failed to get pid from pub key: %v", err) } privkeyb, err := p2p_ci.MarshalPrivateKey(priv) if err != nil { t.Fatalf("failed to get raw priv key: %v", err) } c.Bootstrap = []string{} c.Addresses.Swarm = []string{"/ip6/::/tcp/0"} c.Identity.PeerID = pid.String() c.Identity.PrivKey = base64.StdEncoding.EncodeToString(privkeyb) c.Swarm.ResourceMgr.Enabled = ipfs_cfg.False if datastore == nil { datastore = ds.NewMapDatastore() } dstore := dsync.MutexWrap(datastore) return &ipfs_repo.Mock{ D: dstore, C: c, } } type TestingAPIOpts struct { Logger *zap.Logger Mocknet mocknet.Mocknet Datastore ds.Batching DiscoveryServer *tinder.MockDriverServer } // TestingCoreAPIUsingMockNet returns a fully initialized mocked Core API with the given mocknet func TestingCoreAPIUsingMockNet(ctx context.Context, t testing.TB, opts *TestingAPIOpts) CoreAPIMock { if opts.Logger == nil { opts.Logger = zap.NewNop() } msrv := opts.DiscoveryServer if msrv == nil { t.Log("warning: no discovery available") msrv = tinder.NewMockDriverServer() } datastore := opts.Datastore if datastore == nil { datastore = dsync.MutexWrap(ds.NewMapDatastore()) } repo := TestingRepo(t, ctx, datastore) mrepo := ipfs_mobile.NewRepoMobile("", repo) t.Cleanup(func() { mrepo.Close() }) mnode, err := NewIPFSMobile(ctx, mrepo, &MobileOptions{ HostOption: MockHostOption(opts.Mocknet), RoutingOption: ipfs_p2p.NilRouterOption, ExtraOpts: map[string]bool{ "pubsub": false, }, P2PStaticRelays: []string{}, PeerStorePeers: []string{}, }) t.Cleanup(func() { mnode.Close() }) h := mnode.PeerHost() mockDriver := msrv.Client(h) // enable discovery monitor stinder, err := tinder.NewService(h, opts.Logger, mockDriver) require.NoError(t, err) discAdaptater := tinder.NewDiscoveryAdaptater(opts.Logger, stinder) t.Cleanup(func() { discAdaptater.Close() }) ps, err := pubsub.NewGossipSub(ctx, h, pubsub.WithDiscovery(discAdaptater)) require.NoError(t, err) require.NoError(t, err, "failed to initialize IPFS node mock") require.NotNil(t, ps, "pubsub should not be nil") require.NotNil(t, stinder, "discovery should not be nil") exapi, err := NewExtendedCoreAPIFromNode(mnode.IpfsNode) require.NoError(t, err, "unable to extend core api from node") psapi := NewPubSubAPI(ctx, opts.Logger, ps) exapi = InjectPubSubCoreAPIExtendedAdapter(exapi, psapi) EnableConnLogger(ctx, opts.Logger, mnode.PeerHost()) api := &coreAPIMock{ coreapi: exapi, mocknet: opts.Mocknet, pubsub: ps, node: mnode.IpfsNode, tinder: stinder, } return api } // TestingCoreAPI returns a fully initialized mocked Core API. // If you want to do some tests involving multiple peers you should use // `TestingCoreAPIUsingMockNet` with the same mocknet instead. func TestingCoreAPI(ctx context.Context, t testing.TB) CoreAPIMock { t.Helper() m := mocknet.New() t.Cleanup(func() { m.Close() }) api := TestingCoreAPIUsingMockNet(ctx, t, &TestingAPIOpts{ Mocknet: m, DiscoveryServer: tinder.NewMockDriverServer(), }) return api } func TestingRDVP(ctx context.Context, t testing.TB, h host.Host) (*rendezvous.RendezvousService, func()) { db, err := p2p_rpdb.OpenDB(ctx, ":memory:") require.NoError(t, err) provider, err := rendezvous.NewSyncInMemProvider(h) require.NoError(t, err) svc := rendezvous.NewRendezvousService(h, db, provider) cleanup := func() { _ = db.Close() // dont use me for now as db is open in_memory } return svc, cleanup } type coreAPIMock struct { coreapi ExtendedCoreAPI pubsub *pubsub.PubSub mocknet mocknet.Mocknet node *ipfs_core.IpfsNode tinder *tinder.Service } func (m *coreAPIMock) ConnMgr() ConnMgr { return m.node.PeerHost.ConnManager() } func (m *coreAPIMock) NewStream(ctx context.Context, p p2p_peer.ID, pids ...protocol.ID) (p2pnetwork.Stream, error) { return m.node.PeerHost.NewStream(ctx, p, pids...) } func (m *coreAPIMock) SetStreamHandler(pid protocol.ID, handler p2pnetwork.StreamHandler) { m.node.PeerHost.SetStreamHandler(pid, handler) } func (m *coreAPIMock) RemoveStreamHandler(pid protocol.ID) { m.node.PeerHost.RemoveStreamHandler(pid) } func (m *coreAPIMock) API() ExtendedCoreAPI { return m.coreapi } func (m *coreAPIMock) MockNetwork() mocknet.Mocknet { return m.mocknet } func (m *coreAPIMock) MockNode() *ipfs_core.IpfsNode { return m.node } func (m *coreAPIMock) PubSub() *pubsub.PubSub { return m.pubsub } func (m *coreAPIMock) Tinder() *tinder.Service { return m.tinder } func (m *coreAPIMock) Close() { m.node.Close() } func MockHostOption(mn mocknet.Mocknet) ipfs_p2p.HostOption { return func(id p2p_peer.ID, ps peerstore.Peerstore, _ ...libp2p.Option) (host.Host, error) { blackholeIP6 := net.ParseIP("100::") pkey := ps.PrivKey(id) if pkey == nil { return nil, fmt.Errorf("missing private key for node ID: %s", id) } suffix := id if len(id) > 8 { suffix = id[len(id)-8:] } ip := append(net.IP{}, blackholeIP6...) copy(ip[net.IPv6len-len(suffix):], suffix) a, err := ma.NewMultiaddr(fmt.Sprintf("/ip6/%s/tcp/4242", ip)) if err != nil { return nil, fmt.Errorf("failed to create test multiaddr: %s", err) } ps.AddAddr(id, a, peerstore.PermanentAddrTTL) err = ps.AddPrivKey(id, pkey) if err != nil { return nil, err } err = ps.AddPubKey(id, pkey.GetPublic()) if err != nil { return nil, err } return mn.AddPeerWithPeerstore(id, ps) } } ================================================ FILE: pkg/ipfsutil/util.go ================================================ package ipfsutil import ( "bytes" "fmt" "net" "sort" ma "github.com/multiformats/go-multiaddr" "go.uber.org/multierr" ) type Multiaddrs []ma.Multiaddr func NewMultiaddrs(m []ma.Multiaddr) Multiaddrs { ms := Multiaddrs(m) sort.Sort(ms) return ms } // Len is the number of elements in the collection. func (ms Multiaddrs) Len() int { return len(ms) } // Less reports whether the element with // index i should sort before the element with index j. func (ms Multiaddrs) Less(i, j int) bool { return bytes.Compare(ms[i].Bytes(), ms[j].Bytes()) < 0 } // Swap swaps the elements with indexes i and j. func (ms Multiaddrs) Swap(i, j int) { ms[i], ms[j] = ms[j], ms[i] } // MultiaddrIsEqual return true if both slice are equal func MultiaddrIsEqual(a Multiaddrs, b Multiaddrs) bool { if len(a) != len(b) { return false } for i := range a { if !a[i].Equal(b[i]) { return false } } return true } func ParseAddr(addr string) (ma.Multiaddr, error) { maddr, err := ma.NewMultiaddr(addr) if err == nil { return maddr, nil } // try to get a tcp multiaddr from host:port host, port, err := net.SplitHostPort(addr) if err != nil { return nil, err } if host == "" { host = "127.0.0.1" } addr = fmt.Sprintf("/ip4/%s/tcp/%s/", host, port) return ma.NewMultiaddr(addr) } func ParseAddrs(addrs ...string) ([]ma.Multiaddr, error) { maddrs := make([]ma.Multiaddr, len(addrs)) var err error for i, addr := range addrs { var thisErr error maddrs[i], thisErr = ParseAddr(addr) err = multierr.Append(err, thisErr) } return maddrs, err } ================================================ FILE: pkg/lifecycle/example_test.go ================================================ package lifecycle_test ================================================ FILE: pkg/lifecycle/manager.go ================================================ package lifecycle import ( "context" "sync" "berty.tech/weshnet/v2/internal/notify" ) type State int const ( StateActive State = iota StateInactive ) type Manager struct { currentState State locker *sync.RWMutex notify *notify.Notify groupWaiter sync.WaitGroup } func NewManager(initialState State) *Manager { var locker sync.RWMutex return &Manager{ currentState: initialState, locker: &locker, notify: notify.New(&locker), } } // UpdateState update the current state of the Manager func (m *Manager) UpdateState(state State) { m.locker.Lock() if m.currentState != state { m.currentState = state m.notify.Broadcast() } m.locker.Unlock() } // WaitForStateChange waits until the currentState changes from sourceState or ctx expires. A true value is returned in former case and false in latter. func (m *Manager) WaitForStateChange(ctx context.Context, sourceState State) bool { m.locker.Lock() ok := true for sourceState == m.currentState && ok { // wait until state has been changed or context has been cancel ok = m.notify.Wait(ctx) } m.locker.Unlock() return ok } // GetCurrentState return the current state of the Manager func (m *Manager) GetCurrentState() (state State) { m.locker.RLock() state = m.currentState m.locker.RUnlock() return } // TaskWaitForStateChange is the same as `WaitForStateChange` but also return a // task that can be mark as done by the upper caller to notify he is done // handling the new state, done should always be called to avoid deadlock. // use `WaitForTasks` to wait for task to complete func (m *Manager) TaskWaitForStateChange(ctx context.Context, sourceState State) (Task, bool) { m.locker.Lock() defer m.locker.Unlock() for sourceState == m.currentState { // wait until state has been changed or context has been cancel if ok := m.notify.Wait(ctx); !ok { return nil, false } } m.groupWaiter.Add(1) return &task{t: &m.groupWaiter}, true } // WaitForTasks wait until all tasks returned by `TaskWaitForStateChange` are done func (m *Manager) WaitForTasks() { m.locker.RLock() m.groupWaiter.Wait() // wait for all tasks to be done m.locker.RUnlock() } ================================================ FILE: pkg/lifecycle/task.go ================================================ package lifecycle import "sync" type Task interface { Done() } type task struct { t Task o sync.Once } func (t *task) Done() { t.o.Do(t.t.Done) } ================================================ FILE: pkg/logutil/crypto_utils.go ================================================ package logutil import ( "encoding/base64" "github.com/libp2p/go-libp2p/core/crypto" ) func CryptoKeyToBytes(key crypto.Key) []byte { keyBytes, err := key.Raw() if err != nil { return []byte{0} } return keyBytes } func CryptoKeyToBase64(key crypto.Key) string { bytes := CryptoKeyToBytes(key) return base64.StdEncoding.EncodeToString(bytes) } ================================================ FILE: pkg/logutil/encoders.go ================================================ package logutil import ( "fmt" "go.uber.org/zap/zapcore" ) const ( Black uint8 = iota + 30 Red Green Yellow Blue Magenta Cyan White ) func stableWidthNameEncoder(loggerName string, enc zapcore.PrimitiveArrayEncoder) { enc.AppendString(fmt.Sprintf("%-18s", loggerName)) } func stableWidthCapitalLevelEncoder(l zapcore.Level, enc zapcore.PrimitiveArrayEncoder) { enc.AppendString(fmt.Sprintf("%-5s", l.CapitalString())) } func stableWidthCapitalColorLevelEncoder(l zapcore.Level, enc zapcore.PrimitiveArrayEncoder) { switch l { case zapcore.DebugLevel: enc.AppendString(fmt.Sprintf("\x1b[%dm%s\x1b[0m", Magenta, "DEBUG")) case zapcore.InfoLevel: enc.AppendString(fmt.Sprintf("\x1b[%dm%s\x1b[0m", Blue, "INFO")) case zapcore.WarnLevel: enc.AppendString(fmt.Sprintf("\x1b[%dm%s\x1b[0m", Yellow, "WARN")) case zapcore.ErrorLevel: enc.AppendString(fmt.Sprintf("\x1b[%dm%s\x1b[0m", Red, "ERROR")) case zapcore.DPanicLevel: enc.AppendString(fmt.Sprintf("\x1b[%dm%s\x1b[0m", Red, "DPANIC")) case zapcore.PanicLevel: enc.AppendString(fmt.Sprintf("\x1b[%dm%s\x1b[0m", Red, "PANIC")) case zapcore.FatalLevel: enc.AppendString(fmt.Sprintf("\x1b[%dm%s\x1b[0m", Red, "FATAL")) default: enc.AppendString(fmt.Sprintf("\x1b[%dm%s\x1b[0m", Red, l.CapitalString())) } } ================================================ FILE: pkg/logutil/example_test.go ================================================ package logutil_test import ( "berty.tech/weshnet/v2/pkg/logutil" ) func Example_logall() { logger, cleanup, err := logutil.NewLogger(logutil.NewStdStream("*", "light-console", "stdout")) if err != nil { panic(err) } defer cleanup() logger.Debug("top debug") logger.Info("top info") logger.Warn("top warn") logger.Error("top error") logger.Named("foo").Debug("foo debug") logger.Named("foo").Info("foo info") logger.Named("foo").Warn("foo warn") logger.Named("foo").Error("foo error") // Output: // DEBUG bty logutil/example_test.go:14 top debug // INFO bty logutil/example_test.go:15 top info // WARN bty logutil/example_test.go:16 top warn // ERROR bty logutil/example_test.go:17 top error // DEBUG bty.foo logutil/example_test.go:19 foo debug // INFO bty.foo logutil/example_test.go:20 foo info // WARN bty.foo logutil/example_test.go:21 foo warn // ERROR bty.foo logutil/example_test.go:22 foo error } func Example_logerrors() { logger, cleanup, err := logutil.NewLogger(logutil.NewStdStream("error:*,-*.bar warn:*.bar", "light-console", "stdout")) if err != nil { panic(err) } defer cleanup() logger.Debug("top debug") logger.Info("top info") logger.Warn("top warn") logger.Error("top error") logger.Named("foo").Debug("foo debug") logger.Named("foo").Info("foo info") logger.Named("foo").Warn("foo warn") logger.Named("foo").Error("foo error") logger.Named("foo").Named("bar").Debug("foo.bar debug") logger.Named("foo").Named("bar").Info("foo.bar info") logger.Named("foo").Named("bar").Warn("foo.bar warn") logger.Named("foo").Named("bar").Error("foo.bar error") // Output: // ERROR bty logutil/example_test.go:45 top error // ERROR bty.foo logutil/example_test.go:50 foo error // WARN bty.foo.bar logutil/example_test.go:54 foo.bar warn } ================================================ FILE: pkg/logutil/file.go ================================================ package logutil import ( "fmt" "io" "io/fs" "os" "path/filepath" "regexp" "sort" "strings" "time" "go.uber.org/multierr" "moul.io/u" "berty.tech/weshnet/v2/pkg/errcode" ) func newFileWriteCloser(target, kind string) (io.WriteCloser, error) { var filename string switch { case strings.HasSuffix(target, ".log"): // use the indicated 'path' as filename filename = target default: // automatically create a new file in the 'path' directory following a pattern startTime := time.Now().Format(filePatternDateLayout) filename = filepath.Join( target, fmt.Sprintf("%s-%s.log", kind, startTime), ) // run gc { err := LogfileGC(target, 20) if err != nil { return nil, err } } } if dir := filepath.Dir(filename); !u.DirExists(dir) { err := os.MkdirAll(dir, 0o711) if err != nil { return nil, errcode.ErrCode_TODO.Wrap(err) } } var writer io.WriteCloser if u.FileExists(filename) { var err error writer, err = os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, os.ModeAppend) if err != nil { return nil, errcode.ErrCode_TODO.Wrap(err) } } else { var err error writer, err = os.Create(filename) if err != nil { return nil, errcode.ErrCode_TODO.Wrap(err) } } return writer, nil } type Logfile struct { Dir string Name string Size int64 Kind string Time time.Time Latest bool Errs error `json:"Errs,omitempty"` } func (l Logfile) Path() string { return filepath.Join(l.Dir, l.Name) } const filePatternDateLayout = "2006-01-02T15-04-05.000" var filePatternRegex = regexp.MustCompile(`(?m)^(.*)-(\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}.\d{3}).log$`) func LogfileList(logDir string) ([]*Logfile, error) { files, err := os.ReadDir(logDir) if err != nil { return nil, errcode.ErrCode_TODO.Wrap(err) } infos := make([]fs.FileInfo, 0, len(files)) for _, entry := range files { info, err := entry.Info() if err != nil { return nil, errcode.ErrCode_TODO.Wrap(err) } infos = append(infos, info) } logfiles := []*Logfile{} for _, info := range infos { sub := filePatternRegex.FindStringSubmatch(info.Name()) if sub == nil { continue } t, err := time.Parse(filePatternDateLayout, sub[2]) var errs error if err != nil { errs = multierr.Append(errs, err) } // use os.Stat to get the file size (updated than fs.FileInfo.Size() filepath := filepath.Join(logDir, info.Name()) fi, err := os.Stat(filepath) if err != nil { errs = multierr.Append(errs, err) } logfiles = append(logfiles, &Logfile{ Dir: logDir, Name: info.Name(), Size: fi.Size(), Kind: sub[1], Time: t, Errs: errs, }) } // compute latest if len(logfiles) > 0 { var maxTime time.Time for _, file := range logfiles { if file.Time.After(maxTime) { maxTime = file.Time } } for _, file := range logfiles { if file.Time == maxTime { file.Latest = true } } } return logfiles, nil } func CurrentLogfilePath(target string) (string, error) { filename := "" switch { case strings.HasSuffix(target, ".log"): // use the indicated 'path' as filename filename = target default: // find the latest log file in the 'path' directory following a pattern logfileList, err := LogfileList(target) if err != nil { return "", err } for _, logfile := range logfileList { if logfile.Latest { filename = logfile.Path() break } } } return filename, nil } func LogfileGC(logDir string, max int) error { if !u.DirExists(logDir) { return nil } files, err := LogfileList(logDir) if err != nil { return errcode.ErrCode_TODO.Wrap(err) } if len(files) < max { return nil } sort.Slice(files, func(i, j int) bool { return files[i].Time.Before(files[j].Time) }) var errs error for i := 0; i < len(files)-max; i++ { err := os.Remove(files[i].Path()) if err != nil { errs = multierr.Append(errs, err) } } return errs } ================================================ FILE: pkg/logutil/file_test.go ================================================ package logutil import ( "fmt" "os" "path/filepath" "testing" "time" "github.com/stretchr/testify/require" "moul.io/u" ) func TestLogfile(t *testing.T) { // setup volatile directory for the test tempdir, err := os.MkdirTemp("", "logutil-file") require.NoError(t, err) defer os.RemoveAll(tempdir) // check loading log files from an invalid directory { files, err := LogfileList(filepath.Join(tempdir, "doesnotexist")) require.Error(t, err) require.Nil(t, files) } // check loading files from empty valid directory { files, err := LogfileList(tempdir) require.NoError(t, err) require.Empty(t, files) } // create dummy files { dummyNames := []string{ "2021-05-25T21-12-02.650.log", "cli.info-2021-05-25T21-12-02.aaa.log", "blah.log", } for _, name := range dummyNames { f, err := os.Create(filepath.Join(tempdir, name)) require.NoError(t, err) err = f.Close() require.NoError(t, err) } } // check loading files from valid directory with only dummy files { files, err := LogfileList(tempdir) require.NoError(t, err) require.Empty(t, files) } // create a first logger of kind-1 { writer, err := newFileWriteCloser(tempdir, "kind-1") require.NoError(t, err) require.NotNil(t, writer) _, err = writer.Write([]byte("blah\n")) require.NoError(t, err) err = writer.Close() require.NoError(t, err) } // check loading files from the directory, should have one now { files, err := LogfileList(tempdir) require.NoError(t, err) require.Len(t, files, 1) require.Equal(t, files[0].Dir, tempdir) require.NotEmpty(t, files[0].Name) require.Equal(t, files[0].Path(), filepath.Join(tempdir, files[0].Name)) require.True(t, u.FileExists(files[0].Path())) require.True(t, files[0].Latest) require.Equal(t, files[0].Kind, "kind-1") } // create a second logger of kind-1 { time.Sleep(time.Second) writer, err := newFileWriteCloser(tempdir, "kind-1") require.NoError(t, err) require.NotNil(t, writer) _, err = writer.Write([]byte("blah blah\n")) require.NoError(t, err) err = writer.Close() require.NoError(t, err) } // check loading files from the directory, should have two now { files, err := LogfileList(tempdir) require.NoError(t, err) require.Len(t, files, 2) for _, file := range files { require.Equal(t, file.Dir, tempdir) require.NotEmpty(t, file.Name) require.Equal(t, file.Path(), filepath.Join(tempdir, file.Name)) require.True(t, u.FileExists(file.Path())) } } // try to gc with fewer files than the limit { err := LogfileGC(tempdir, 10) require.NoError(t, err) } // create 10 new files { for i := 0; i < 10; i++ { writer, err := newFileWriteCloser(tempdir, fmt.Sprintf("hello-%d", i)) require.NoError(t, err) err = writer.Close() require.NoError(t, err) } } // check loading files from the directory, should have twelve now { files, err := LogfileList(tempdir) require.NoError(t, err) require.Len(t, files, 12) for _, file := range files { require.Equal(t, file.Dir, tempdir) require.NotEmpty(t, file.Name) require.Equal(t, file.Path(), filepath.Join(tempdir, file.Name)) require.True(t, u.FileExists(file.Path())) } } // try to gc with fewer files than the limit { err := LogfileGC(tempdir, 10) require.NoError(t, err) } // check loading files from the directory, should have ten now { files, err := LogfileList(tempdir) require.NoError(t, err) require.Len(t, files, 10) } // try to gc with the current amount of files { err := LogfileGC(tempdir, 10) require.NoError(t, err) } // check loading files from the directory, should still have ten { files, err := LogfileList(tempdir) require.NoError(t, err) require.Len(t, files, 10) } // try to gc with only one { err := LogfileGC(tempdir, 1) require.NoError(t, err) } // check loading files from the directory, should now have only one { files, err := LogfileList(tempdir) require.NoError(t, err) require.Len(t, files, 1) } } ================================================ FILE: pkg/logutil/grpc_logger.go ================================================ package logutil import ( "sync" grpc_zap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap" "go.uber.org/zap" ) var ( // grpc logger should be set only once. // without this singleton, we can raise race conditions in unit tests => https://github.com/grpc/grpc-go/issues/1084 grpcLoggerConfigured bool muGRPCLoggerConfigured sync.Mutex ) func ReplaceGRPCLogger(l *zap.Logger) { muGRPCLoggerConfigured.Lock() if !grpcLoggerConfigured { grpc_zap.ReplaceGrpcLoggerV2(l) grpcLoggerConfigured = true } muGRPCLoggerConfigured.Unlock() } ================================================ FILE: pkg/logutil/logger_native.go ================================================ package logutil import ( "fmt" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) type nativeCore struct { subsystem string enc zapcore.Encoder } func NewNativeDriverCore(subsystem string, enc zapcore.Encoder) zapcore.Core { return &nativeCore{subsystem: subsystem, enc: enc} } func (nc *nativeCore) Enabled(zapcore.Level) bool { return true } func (nc *nativeCore) With([]zapcore.Field) zapcore.Core { return &nativeCore{enc: nc.enc} } func (nc *nativeCore) Check(entry zapcore.Entry, checked *zapcore.CheckedEntry) *zapcore.CheckedEntry { return checked.AddCore(entry, nc) } func (nc *nativeCore) Write(entry zapcore.Entry, fields []zapcore.Field) error { buff, err := nc.enc.EncodeEntry(entry, fields) if err != nil { return err } NativeLog( entry.Level, fmt.Sprintf("%s.%s", nc.subsystem, entry.LoggerName), buff.String(), ) return nil } func (nc *nativeCore) Sync() error { return nil } func NewNativeLogger(subsystem string) *zap.Logger { // native logger nativeEncoderConfig := zap.NewDevelopmentEncoderConfig() nativeEncoderConfig.LevelKey = "" nativeEncoderConfig.TimeKey = "" nativeEncoderConfig.NameKey = "" nativeEncoderConfig.CallerKey = "" nativeEncoder := zapcore.NewConsoleEncoder(nativeEncoderConfig) core := NewNativeDriverCore(subsystem, nativeEncoder) // create logger logger := zap.New(core) return logger } ================================================ FILE: pkg/logutil/logger_native_android.go ================================================ //go:build android package logutil /* #cgo LDFLAGS: -llog #include */ import "C" import ( "fmt" "go.uber.org/zap/zapcore" ) func NativeLog(logLevel zapcore.Level, namespace string, message string) { var level C.int = C.ANDROID_LOG_INFO switch logLevel { case zapcore.DebugLevel: level = C.ANDROID_LOG_DEBUG case zapcore.InfoLevel: level = C.ANDROID_LOG_INFO case zapcore.WarnLevel: level = C.ANDROID_LOG_WARN case zapcore.ErrorLevel: level = C.ANDROID_LOG_ERROR } C.__android_log_write(level, C.CString(namespace), C.CString(fmt.Sprintf("[%s] %s", logLevel.CapitalString(), message))) } ================================================ FILE: pkg/logutil/logger_native_darwin.go ================================================ //go:build darwin // +build darwin package logutil /* #import const int DEBUG = 0; const int INFO = 1; const int WARN = 2; const int ERROR = 3; void os_log_wrapper(int level, os_log_t log, char *s) { switch (level) { case DEBUG: os_log_debug(log, "%{public}s", s); break ; case WARN: os_log_error(log, "%{public}s", s); break ; case ERROR: os_log_fault(log, "%{public}s", s); break ; default: os_log_info(log, "%{public}s", s); } } */ import "C" import ( "fmt" "go.uber.org/zap/zapcore" ) func NativeLog(logLevel zapcore.Level, namespace string, message string) { var level C.int = C.INFO switch logLevel { case zapcore.DebugLevel: level = C.DEBUG case zapcore.WarnLevel: level = C.WARN case zapcore.ErrorLevel: level = C.ERROR default: level = C.INFO } log := C.os_log_create(C.CString(namespace), C.CString(namespace)) C.os_log_wrapper(level, log, C.CString(fmt.Sprintf("[%s] %s", logLevel.CapitalString(), message))) } ================================================ FILE: pkg/logutil/logger_native_other.go ================================================ //go:build !darwin && !android package logutil import ( "fmt" "os" "go.uber.org/zap/zapcore" ) func NativeLog(logLevel zapcore.Level, namespace string, message string) { fmt.Fprintf(os.Stderr, "[%s] [%s] %s\n", logLevel.CapitalString(), namespace, message) } ================================================ FILE: pkg/logutil/logutil.go ================================================ package logutil import ( "fmt" "strings" ipfs_log "github.com/ipfs/go-log/v2" "go.uber.org/zap" "go.uber.org/zap/zapcore" "moul.io/u" "moul.io/zapfilter" "berty.tech/weshnet/v2/pkg/errcode" ) const ( consoleEncoding = "console" jsonEncoding = "json" ) func NewLogger(streams ...Stream) (*zap.Logger, func(), error) { cores := []zapcore.Core{} cleanup := func() {} withIPFS := false withIPFSDebug := false for _, opts := range streams { if opts.filters == "" { continue } var core zapcore.Core // configure zap var config zap.Config switch strings.ToLower(opts.format) { case "": config = zap.NewProductionConfig() case "json": config = zap.NewProductionConfig() config.Development = true config.Encoding = jsonEncoding case "light-json": config = zap.NewProductionConfig() config.Encoding = jsonEncoding config.EncoderConfig.TimeKey = "" config.EncoderConfig.EncodeLevel = stableWidthCapitalLevelEncoder config.Development = false config.DisableStacktrace = true config.Sampling = &zap.SamplingConfig{Initial: 100, Thereafter: 100} case "light-console": config = zap.NewDevelopmentConfig() config.Encoding = consoleEncoding config.EncoderConfig.TimeKey = "" config.EncoderConfig.EncodeLevel = stableWidthCapitalLevelEncoder config.DisableStacktrace = true config.EncoderConfig.EncodeName = stableWidthNameEncoder config.Development = false config.Sampling = &zap.SamplingConfig{Initial: 100, Thereafter: 100} case "light-color": config = zap.NewDevelopmentConfig() config.Encoding = consoleEncoding config.EncoderConfig.TimeKey = "" config.EncoderConfig.EncodeLevel = stableWidthCapitalColorLevelEncoder config.DisableStacktrace = true config.EncoderConfig.EncodeName = stableWidthNameEncoder config.Development = false config.Sampling = &zap.SamplingConfig{Initial: 100, Thereafter: 100} case "console": config = zap.NewDevelopmentConfig() config.Encoding = consoleEncoding config.EncoderConfig.EncodeTime = zapcore.RFC3339TimeEncoder config.EncoderConfig.EncodeLevel = stableWidthCapitalLevelEncoder config.DisableStacktrace = true config.EncoderConfig.EncodeName = stableWidthNameEncoder config.Development = true case "color": config = zap.NewDevelopmentConfig() config.Encoding = consoleEncoding config.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder config.EncoderConfig.EncodeDuration = zapcore.StringDurationEncoder config.EncoderConfig.EncodeLevel = stableWidthCapitalColorLevelEncoder config.DisableStacktrace = true config.EncoderConfig.EncodeName = stableWidthNameEncoder config.Development = true default: return nil, nil, fmt.Errorf("unknown log format: %q", opts.format) } config.Level = zap.NewAtomicLevelAt(zap.DebugLevel) var enc zapcore.Encoder switch config.Encoding { case consoleEncoding: enc = zapcore.NewConsoleEncoder(config.EncoderConfig) case jsonEncoding: enc = zapcore.NewJSONEncoder(config.EncoderConfig) } switch opts.kind { case typeStd: switch opts.path { case "": case "stdout", "stderr": config.OutputPaths = []string{opts.path} default: config.OutputPaths = []string{opts.path} } logger, err := config.Build() if err != nil { return nil, nil, err } core = logger.Core() case typeRing: ring := opts.ring.SetEncoder(enc) core = ring case typeFile: writer, err := newFileWriteCloser(opts.path, opts.sessionKind) if err != nil { return nil, nil, errcode.ErrCode_TODO.Wrap(err) } w := zapcore.AddSync(writer) core = zapcore.NewCore(enc, w, config.Level) cleanup = u.CombineFuncs(cleanup, func() { _ = writer.Close() }) case typeCustom: core = opts.baseLogger.Core() default: return nil, nil, fmt.Errorf("unknown logger type: %q", opts.kind) } filter, err := zapfilter.ParseRules(opts.filters) if err != nil { return nil, nil, err } filtered := zapfilter.NewFilteringCore(core, filter) if !withIPFS && zapfilter.CheckAnyLevel(zap.New(filtered).Named("ipfs")) { withIPFS = true if zapfilter.CheckLevel(zap.New(filtered).Named("ipfs"), zap.DebugLevel) { withIPFSDebug = true } } cores = append(cores, filtered) } if len(cores) == 0 { return zap.NewNop(), cleanup, nil } // combine cores tee := zap.New( zapcore.NewTee(cores...), zap.AddCaller(), ) if withIPFS { if withIPFSDebug { ipfs_log.SetDebugLogging() } ipfs_log.SetPrimaryCore(tee.Core()) } else { ipfs_log.SetPrimaryCore(zapcore.NewNopCore()) } cleanup = u.CombineFuncs(cleanup, func() { _ = tee.Sync() }) return tee.Named("bty"), cleanup, nil } ================================================ FILE: pkg/logutil/logutil_test.go ================================================ package logutil_test import ( "bufio" "io" "os" "path/filepath" "runtime" "strings" "testing" "github.com/stretchr/testify/require" "moul.io/u" "moul.io/zapring" "berty.tech/weshnet/v2/pkg/logutil" ) func TestTypeStd(t *testing.T) { if runtime.GOOS == "windows" { t.Skip("unittest not consistent on windows, skipping.") } closer, err := u.CaptureStdoutAndStderr() require.NoError(t, err) logger, cleanup, err := logutil.NewLogger( logutil.NewStdStream("*", "light-console", "stdout"), ) require.NoError(t, err) defer cleanup() logger.Info("hello world!") logger.Warn("hello world!") logger.Sync() lines := strings.Split(strings.TrimSpace(closer()), "\n") require.Equal(t, 2, len(lines)) require.Equal(t, "INFO \tbty \tlogutil/logutil_test.go:33\thello world!", lines[0]) require.Equal(t, "WARN \tbty \tlogutil/logutil_test.go:34\thello world!", lines[1]) } func TestTypeRing(t *testing.T) { if runtime.GOOS == "windows" { t.Skip("unittest not consistent on windows, skipping.") } closer, err := u.CaptureStdoutAndStderr() require.NoError(t, err) ring := zapring.New(10 * 1024 * 1024) // 10MB ring-buffer defer ring.Close() logger, cleanup, err := logutil.NewLogger( logutil.NewRingStream("*", "light-console", ring), ) defer cleanup() require.NoError(t, err) logger.Info("hello world!") logger.Warn("hello world!") logger.Sync() require.Empty(t, closer()) r, w := io.Pipe() go func() { _, err := ring.WriteTo(w) require.True(t, err == nil || err == io.EOF) w.Close() }() scanner := bufio.NewScanner(r) scanner.Scan() require.Equal(t, "INFO \tbty \tlogutil/logutil_test.go:59\thello world!", scanner.Text()) scanner.Scan() require.Equal(t, "WARN \tbty \tlogutil/logutil_test.go:60\thello world!", scanner.Text()) } func TestTypeFile(t *testing.T) { t.Run("fullpath", func(t *testing.T) { if runtime.GOOS == "windows" { t.Skip("unittest not consistent on windows, skipping.") } tempdir, err := os.MkdirTemp("", "logutil-file") require.NoError(t, err) filename := filepath.Join(tempdir, "test.log") closer, err := u.CaptureStdoutAndStderr() require.NoError(t, err) logger, cleanup, err := logutil.NewLogger( logutil.NewFileStream("*", "light-console", filename, ""), ) require.NoError(t, err) defer cleanup() logger.Info("hello world!") logger.Warn("hello world!") logger.Sync() require.Empty(t, closer()) content, err := os.ReadFile(filename) require.NoError(t, err) lines := strings.Split(string(content), "\n") require.Equal(t, 3, len(lines)) require.Equal(t, "INFO \tbty \tlogutil/logutil_test.go:98\thello world!", lines[0]) require.Equal(t, "WARN \tbty \tlogutil/logutil_test.go:99\thello world!", lines[1]) require.Equal(t, "", lines[2]) }) t.Run("pattern", func(t *testing.T) { if runtime.GOOS == "windows" { t.Skip("unittest not consistent on windows, skipping.") } tempdir, err := os.MkdirTemp("", "logutil-file") require.NoError(t, err) closer, err := u.CaptureStdoutAndStderr() require.NoError(t, err) logger, cleanup, err := logutil.NewLogger( logutil.NewFileStream("*", "light-console", tempdir, "just.a.test"), ) require.NoError(t, err) defer cleanup() logger.Info("hello world!") logger.Warn("hello world!") logger.Sync() require.Empty(t, closer()) files, err := os.ReadDir(tempdir) require.NoError(t, err) require.Len(t, files, 1) content, err := os.ReadFile(filepath.Join(tempdir, files[0].Name())) require.NoError(t, err) lines := strings.Split(string(content), "\n") require.Equal(t, 3, len(lines)) require.Equal(t, "INFO \tbty \tlogutil/logutil_test.go:130\thello world!", lines[0]) require.Equal(t, "WARN \tbty \tlogutil/logutil_test.go:131\thello world!", lines[1]) require.Equal(t, "", lines[2]) }) } func TestMultiple(t *testing.T) { if runtime.GOOS == "windows" { t.Skip("unittest not consistent on windows, skipping.") } tempdir, err := os.MkdirTemp("", "logutil-file") require.NoError(t, err) defer os.RemoveAll(tempdir) // ring ring := zapring.New(10 * 1024 * 1024) // 10MB ring-buffer defer ring.Close() closer, err := u.CaptureStdoutAndStderr() require.NoError(t, err) logger, cleanup, err := logutil.NewLogger( logutil.NewFileStream("*", "light-console", filepath.Join(tempdir, "test.log"), ""), logutil.NewRingStream("*", "light-console", ring), logutil.NewStdStream("*", "light-console", "stdout"), ) require.NoError(t, err) defer cleanup() logger.Info("hello world!") logger.Warn("hello world!") logger.Sync() // std { lines := strings.Split(strings.TrimSpace(closer()), "\n") require.Equal(t, 2, len(lines)) require.Equal(t, "INFO \tbty \tlogutil/logutil_test.go:174\thello world!", lines[0]) require.Equal(t, "WARN \tbty \tlogutil/logutil_test.go:175\thello world!", lines[1]) } // file { content, err := os.ReadFile(filepath.Join(tempdir, "test.log")) require.NoError(t, err) lines := strings.Split(string(content), "\n") require.Equal(t, 3, len(lines)) require.Equal(t, "INFO \tbty \tlogutil/logutil_test.go:174\thello world!", lines[0]) require.Equal(t, "WARN \tbty \tlogutil/logutil_test.go:175\thello world!", lines[1]) require.Equal(t, "", lines[2]) } // ring { r, w := io.Pipe() go func() { _, err := ring.WriteTo(w) require.True(t, err == nil || err == io.EOF) w.Close() }() scanner := bufio.NewScanner(r) scanner.Scan() require.Equal(t, "INFO \tbty \tlogutil/logutil_test.go:174\thello world!", scanner.Text()) scanner.Scan() require.Equal(t, "WARN \tbty \tlogutil/logutil_test.go:175\thello world!", scanner.Text()) } // FIXME: test that each logger can have its own format and filters } // FIXME: add unit test for NewCustomStream ================================================ FILE: pkg/logutil/private_field.go ================================================ package logutil import ( crand "crypto/rand" "crypto/sha256" "encoding/hex" "fmt" "sync" "go.uber.org/zap" ) var ( global *PrivateField mu sync.RWMutex ) type PrivateField struct { Namespace []byte Enabled bool } func (p *PrivateField) hash(value string) string { hash := sha256.New() if _, err := hash.Write(p.Namespace); err != nil { return "unrepresentable" } if _, err := hash.Write([]byte(value)); err != nil { return "unrepresentable" } hashed := hash.Sum(nil) return hex.EncodeToString(hashed) } func (p *PrivateField) PrivateString(key string, value string) zap.Field { if p.Enabled { return zap.String(key, p.hash(value)) } return zap.String(key, value) } func (p *PrivateField) PrivateStringer(key string, value fmt.Stringer) zap.Field { if p.Enabled { return zap.String(key, p.hash(value.String())) } return zap.Stringer(key, value) } func (p *PrivateField) PrivateStrings(key string, values []string) zap.Field { if p.Enabled { strings := make([]string, len(values)) for i := range values { strings[i] = p.hash(values[i]) } return zap.Strings(key, strings) } return zap.Strings(key, values) } func (p *PrivateField) PrivateAny(key string, value any) zap.Field { if p.Enabled { return zap.String(key, p.hash(fmt.Sprintf("%+v", value))) } return zap.Any(key, value) } func (p *PrivateField) PrivateBinary(key string, value []byte) zap.Field { if p.Enabled { return zap.String(key, p.hash(hex.EncodeToString(value))) } return zap.Binary(key, value) } func PrivateStrings(key string, value []string) zap.Field { mu.RLock() g := global mu.RUnlock() return g.PrivateStrings(key, value) } func PrivateString(key string, value string) zap.Field { mu.RLock() g := global mu.RUnlock() return g.PrivateString(key, value) } func PrivateStringer(key string, value fmt.Stringer) zap.Field { mu.RLock() g := global mu.RUnlock() return g.PrivateStringer(key, value) } func PrivateAny(key string, value any) zap.Field { mu.RLock() g := global mu.RUnlock() return g.PrivateAny(key, value) } func PrivateBinary(key string, value []byte) zap.Field { mu.RLock() g := global mu.RUnlock() return g.PrivateBinary(key, value) } func SetGlobal(namespace []byte, enabled bool) { mu.Lock() global = &PrivateField{ Enabled: enabled, Namespace: namespace, } mu.Unlock() } func DisablePrivateFields() { SetGlobal(nil, false) } func init() { // nolint:gochecknoinits namespace := make([]byte, 32) _, err := crand.Reader.Read(namespace) if err != nil { panic(err) } SetGlobal(namespace, true) } ================================================ FILE: pkg/logutil/stream.go ================================================ package logutil import ( "go.uber.org/zap" "moul.io/zapring" ) const ( typeStd = "std" typeRing = "ring" typeFile = "file" typeCustom = "custom" ) type Stream struct { kind string filters string format string path string ring *zapring.Core sessionKind string baseLogger *zap.Logger } func NewStdStream(filters, format, path string) Stream { return Stream{ kind: typeStd, filters: filters, format: format, path: path, } } func NewRingStream(filters, format string, ring *zapring.Core) Stream { return Stream{ kind: typeRing, filters: filters, format: format, ring: ring, } } // NewFileStream creates a new file stream backed by Lumberjack with sane default values. // // Usually, Lumberjack is used as a rolling log file and is intended to be reused from a session to another, // In Berty, we want one file per session named with the start time instead of the rotation time. // // If the provided path is a directory, it will create files in that directory with the following pattern: // `/-.log`. // // If the provided path is a path finishing with ".log", then, the path will be taken as it, // instead of creating a new file, it will append new lines to the existing one; // this can be particularly useful to keep a `tail -f` running. func NewFileStream(filters, format, path, sessionKind string) Stream { return Stream{ kind: typeFile, filters: filters, format: format, path: path, sessionKind: sessionKind, } } func NewCustomStream(filters string, logger *zap.Logger) Stream { return Stream{ kind: typeCustom, filters: filters, baseLogger: logger, } } ================================================ FILE: pkg/multipeer-connectivity-driver/bridge_darwin.go ================================================ //go:build darwin && cgo && !catalyst && !noproximitytransport package mc import ( "go.uber.org/zap" native "berty.tech/weshnet/v2/pkg/multipeer-connectivity-driver/driver" proximity "berty.tech/weshnet/v2/pkg/proximitytransport" ) const Supported = true type Driver struct { protocolCode int protocolName string defaultAddr string } // Driver is a proximity.ProximityDriver var _ proximity.ProximityDriver = (*Driver)(nil) func NewDriver(logger *zap.Logger) proximity.ProximityDriver { if logger == nil { logger = zap.NewNop() } else { logger = logger.Named("MC") logger.Debug("NewDriver()") native.MCUseExternalLogger() } native.Logger = logger native.ProtocolName = ProtocolName return &Driver{ protocolCode: ProtocolCode, protocolName: ProtocolName, defaultAddr: DefaultAddr, } } func (d *Driver) Start(localPID string) { native.Start(localPID) } func (d *Driver) Stop() { native.Stop() } func (d *Driver) DialPeer(remotePID string) bool { return native.DialPeer(remotePID) } func (d *Driver) SendToPeer(remotePID string, payload []byte) bool { return native.SendToPeer(remotePID, payload) } func (d *Driver) CloseConnWithPeer(remotePID string) { native.CloseConnWithPeer(remotePID) } func (d *Driver) ProtocolCode() int { return d.protocolCode } func (d *Driver) ProtocolName() string { return d.protocolName } func (d *Driver) DefaultAddr() string { return d.defaultAddr } ================================================ FILE: pkg/multipeer-connectivity-driver/bridge_unsupported.go ================================================ //go:build !darwin || (darwin && !cgo) || catalyst || noproximitytransport package mc import ( "go.uber.org/zap" proximity "berty.tech/weshnet/v2/pkg/proximitytransport" ) const Supported = false // Noop implementation for platform that are not Darwin func NewDriver(logger *zap.Logger) proximity.ProximityDriver { logger = logger.Named("MC") logger.Info("NewDriver(): incompatible system") return proximity.NewNoopProximityDriver(ProtocolCode, ProtocolName, DefaultAddr) } ================================================ FILE: pkg/multipeer-connectivity-driver/const.go ================================================ package mc const ( DefaultAddr = "/mc/Qmeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" ProtocolCode = 0x0043 ProtocolName = "mc" ) ================================================ FILE: pkg/multipeer-connectivity-driver/driver/Logger.h ================================================ // // Logger.h // BertyBridgeDemo // // Created by Rémi BARBERO on 08/12/2021. // #import #import NS_ASSUME_NONNULL_BEGIN #define SENSITIVE_MASK @"####" // Log levels typedef NS_ENUM(uint8_t, level) { Debug, Info, Warn, Error, }; @interface MC_Logger : NSObject @property (nonatomic, strong, nonnull) os_log_t logger; @property (readwrite) BOOL showSensitiveData; @property (readwrite) BOOL useExternalLogger; - (instancetype __nonnull)initLocalLoggerWithSubSystem:(const char *)subSystem andCategorie:(const char*)categorie showSensitiveData:(BOOL)showSensitiveData; - (instancetype __nonnull)initWithExternalLoggerAndShowSensitiveData:(BOOL)showSensitiveData; - (void)log:(enum level)level withFormat:(NSString *__nonnull)format withArgs:(va_list)args; - (void)d:(NSString *__nonnull)format, ...; - (void)i:(NSString *__nonnull)format, ...; - (void)e:(NSString *__nonnull)format, ...; - (BOOL)showSensitiveData; - (BOOL)useExternalLogger; - (NSString *__nonnull)SensitiveNSObject:(id __nonnull)data; - (NSString *__nonnull)SensitiveString:(const char *)data; @end @compatibility_alias Logger MC_Logger; NS_ASSUME_NONNULL_END ================================================ FILE: pkg/multipeer-connectivity-driver/driver/Logger.m ================================================ // +build darwin,!noproximitytransport // // Logger.m // BertyBridgeDemo // // Created by Rémi BARBERO on 08/12/2021. // #import #import "Logger.h" #import "mc-driver.h" @implementation Logger - (instancetype __nonnull)initLocalLoggerWithSubSystem:(const char *)subSystem andCategorie:(const char*)categorie showSensitiveData:(BOOL)showSensitiveData { self = [super init]; if (self) { _logger = os_log_create(subSystem, categorie); _useExternalLogger = FALSE; _showSensitiveData = showSensitiveData; } return self; } - (instancetype __nonnull)initWithExternalLoggerAndShowSensitiveData:(BOOL)showSensitiveData { self = [super init]; if (self) { _logger = nil; _useExternalLogger = TRUE; _showSensitiveData = showSensitiveData; } return self; } - (void)log:(enum level)level withFormat:(NSString *__nonnull)format withArgs:(va_list)args { NSString *message = [[NSString alloc] initWithFormat:format arguments:args]; if (self.useExternalLogger) { MCBridgeLog(level, message); } else { if (self.logger == nil) { NSLog(@"log error: logger is not set"); } else { uint8_t osLevel; switch (level) { case Debug: osLevel = OS_LOG_TYPE_DEBUG; break ; case Info: osLevel = OS_LOG_TYPE_INFO; break ; case Error: osLevel = OS_LOG_TYPE_ERROR; break ; default: osLevel = OS_LOG_TYPE_DEFAULT; break ; } os_log_with_type(self.logger, osLevel, "%@", message); } } [message release]; } - (void)d:(NSString *__nonnull)format, ... { va_list args; va_start(args, format); [self log:Debug withFormat:format withArgs:args]; va_end(args); } - (void)i:(NSString *__nonnull)format, ... { va_list args; va_start(args, format); [self log:Info withFormat:format withArgs:args]; va_end(args); } - (void)e:(NSString *__nonnull)format, ... { va_list args; va_start(args, format); [self log:Error withFormat:format withArgs:args]; va_end(args); } - (NSString *__nonnull)SensitiveNSObject:(id __nonnull)data { if (self.showSensitiveData) { return [NSString stringWithFormat:@"%@", data]; } else { return SENSITIVE_MASK; } } - (NSString *__nonnull)SensitiveString:(const char *)data { if (data == nil) { return @""; } if (self.showSensitiveData) { return [NSString stringWithFormat:@"%s", data]; } else { return SENSITIVE_MASK; } } @end ================================================ FILE: pkg/multipeer-connectivity-driver/driver/MCManager.h ================================================ // // MCManager.h // driver // // Created by Rémi BARBERO on 31/03/2020. // Copyright © 2020 Rémi BARBERO. All rights reserved. // #import #import #import "Logger.h" NS_ASSUME_NONNULL_BEGIN @interface MCManager : NSObject @property (nonatomic, strong) MCSession *mSession; @property (nonatomic, strong, nullable) MCNearbyServiceAdvertiser *mServiceAdvertiser; @property (nonatomic, strong, nullable) MCNearbyServiceBrowser *mServiceBrowser; @property (nonatomic, strong) MCPeerID *mPeerID; @property (nonatomic, strong, nullable) Logger *logger; - (MCPeerID *)getMCPeerID:(NSString *)peerID; - (id)init:(NSString *)peerID useExternalLogger:(BOOL)useExternalLogger; - (int)startServiceAdvertiser; - (int)startServiceBrowser; - (void)stopServiceAdvertiser; - (void)stopServiceBrowser; - (void)closeSessions; - (int)sendToPeer:(NSString *)peerID data:(NSData *)data; - (MCPeerID *)getPeer:(NSString *)peerID; @end NS_ASSUME_NONNULL_END ================================================ FILE: pkg/multipeer-connectivity-driver/driver/MCManager.m ================================================ // +build darwin,!noproximitytransport #import #import "MCManager.h" #import "mc-driver.h" NSString *BERTY_DRIVER_MC = @"berty-mc"; @implementation MCManager // MCPeerID must be unique and stable over time so we need to archive it after // the first time it's created. // https://developer.apple.com/documentation/multipeerconnectivity/mcpeerid - (MCPeerID *)getMCPeerID:(NSString *)appPID { NSString *kAppPID = @"berty-peerID"; NSString *kPIDData = @"berty-PIDData"; NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; NSString *oldAppPID = [defaults stringForKey:kAppPID]; MCPeerID *peerID; NSData *peerIDData; NSError *error; if ([oldAppPID isEqualToString:appPID]) { peerIDData = [defaults dataForKey:kPIDData]; if ((peerID = [NSKeyedUnarchiver unarchivedObjectOfClass:[MCPeerID class] fromData:peerIDData error:&error])) { return (peerID); } [self.logger d:@"getMCPeerID unarchive error: %@", error]; } peerID = [[MCPeerID alloc] initWithDisplayName:appPID]; if ((peerIDData = [NSKeyedArchiver archivedDataWithRootObject:peerID requiringSecureCoding:true error:&error])) { [defaults setObject:peerIDData forKey:kPIDData]; [defaults setObject:appPID forKey:kAppPID]; [defaults synchronize]; } else { [self.logger d:@"getMCPeerID archive error: %@", error]; } return (peerID); } - (id)init:(NSString *)peerID useExternalLogger:(BOOL)useExternalLogger { if (self = [super init]) { _mPeerID = [[MCPeerID alloc] initWithDisplayName:peerID]; BOOL showSensitiveData = FALSE; if (useExternalLogger) { _logger = [[Logger alloc] initWithExternalLoggerAndShowSensitiveData:showSensitiveData]; } else { _logger = [[Logger alloc] initLocalLoggerWithSubSystem:"tech.berty.bty" andCategorie:"MC" showSensitiveData:showSensitiveData]; } if (!(self.mSession = [[MCSession alloc] initWithPeer:self.mPeerID securityIdentity:nil encryptionPreference:MCEncryptionRequired])) { [self.logger d:@"MCSession init failed"]; return (self = nil); } self.mSession.delegate = self; } return (self); } - (int)startServiceAdvertiser { if (!(self.mServiceAdvertiser = [[MCNearbyServiceAdvertiser alloc] initWithPeer:self.mPeerID discoveryInfo:nil serviceType:BERTY_DRIVER_MC])) { [self.logger d:@"MCNearbyServiceAdvertiser init failed"]; return (0); } self.mServiceAdvertiser.delegate = self; [self.mServiceAdvertiser startAdvertisingPeer]; return (1); } - (int)startServiceBrowser { if (!(self.mServiceBrowser = [[MCNearbyServiceBrowser alloc] initWithPeer:self.mPeerID serviceType:BERTY_DRIVER_MC])) { [self.logger d:@"MCNearbyServiceBrowser init failed"]; return (0); } self.mServiceBrowser.delegate = self; [self.mServiceBrowser startBrowsingForPeers]; return (1); } - (void)stopServiceAdvertiser { [self.mServiceAdvertiser stopAdvertisingPeer]; self.mServiceAdvertiser = nil; } - (void)stopServiceBrowser { [self.mServiceBrowser stopBrowsingForPeers]; self.mServiceBrowser = nil; } - (void)closeSessions { [self.mSession disconnect]; } - (int)sendToPeer: (NSString *)peerID data:(NSData *)data { NSError *error = nil; MCPeerID *peer; if ((peer = [self getPeer:peerID])) { NSArray *array = @[peer]; if ([self.mSession sendData:data toPeers:array withMode:MCSessionSendDataReliable error:&error]) { return (1); } NSString *description = [error localizedDescription]; NSString *reason = [error localizedFailureReason] ? [error localizedFailureReason] : NSLocalizedString(@"Unknown reason", nil); [self.logger d:@"sendToPeer error: %@: %@", description, reason]; } return (0); } - (MCPeerID *)getPeer:(NSString *)peerID { NSArray *peers = [self.mSession connectedPeers]; for (MCPeerID *peer in peers) { if ([[peer displayName] isEqualToString:peerID]) { return (peer); } } return (nil); } /* MCSessionDelegate */ - (void)session:(MCSession *)session peer:(MCPeerID *)peerID didChangeState:(MCSessionState)state{ switch (state) { case MCSessionStateConnecting: [self.logger d:@"Connecting: %@", [self.logger SensitiveNSObject:[peerID displayName]]]; break; case MCSessionStateConnected: [self.logger i:@"Connected: %@", [self.logger SensitiveNSObject:[peerID displayName]]]; BridgeHandleFoundPeer([peerID displayName]); break; case MCSessionStateNotConnected: [self.logger i:@"Not connected: %@", [self.logger SensitiveNSObject:[peerID displayName]]]; BridgeHandleLostPeer([peerID displayName]); break; } } - (void)session:(MCSession *)session didReceiveData:(NSData *)data fromPeer:(MCPeerID *)peerID{ BridgeReceiveFromPeer([peerID displayName], data); } - (void)session:(MCSession *)session didStartReceivingResourceWithName:(NSString *)resourceName fromPeer:(MCPeerID *)peerID withProgress:(NSProgress *)progress{ } - (void)session:(MCSession *)session didFinishReceivingResourceWithName:(NSString *)resourceName fromPeer:(MCPeerID *)peerID atURL:(NSURL *)localURL withError:(NSError *)error{ } - (void)session:(MCSession *)session didReceiveStream:(NSInputStream *)stream withName:(NSString *)streamName fromPeer:(MCPeerID *)peerID{ } /* MCNearbyServiceAdvertiserDelegate */ - (void)advertiser:(MCNearbyServiceAdvertiser *)advertiser didNotStartAdvertisingPeer:(NSError *)error { [self.logger d:@"didNotStartAdvertisingPeer: %@", error]; } - (void)advertiser:(MCNearbyServiceAdvertiser *)advertiser didReceiveInvitationFromPeer:(MCPeerID *)peerID withContext:(NSData *)context invitationHandler:(void (^)(BOOL, MCSession * _Nullable))invitationHandler { [self.logger d:@"didReceiveInvationFromPeer: %@", [self.logger SensitiveNSObject:[peerID displayName]]]; invitationHandler(true, self.mSession); } /* MCNearbyServiceBrowserDelegate */ - (void)browser:(MCNearbyServiceBrowser *)browser lostPeer:(MCPeerID *)peerID { [self.logger d:@"lostPeer: %@", [self.logger SensitiveNSObject:[peerID displayName]]]; } - (void)browser:(MCNearbyServiceBrowser *)browser didNotStartBrowsingForPeers:(NSError *)error { [self.logger d:@"didNotStartBrowsingForPeers: %@", error]; } - (void)browser:(MCNearbyServiceBrowser *)browser foundPeer:(MCPeerID *)peerID withDiscoveryInfo:(NSDictionary *)info { [self.logger d:@"foundPeer: %@", [self.logger SensitiveNSObject:[peerID displayName]]]; [browser invitePeer:peerID toSession:self.mSession withContext:nil timeout:10]; } @end ================================================ FILE: pkg/multipeer-connectivity-driver/driver/cgo_bridge.go ================================================ //go:build darwin && cgo && !catalyst && !noproximitytransport // +build darwin,cgo,!catalyst,!noproximitytransport package driver /* #cgo CFLAGS: -x objective-c #cgo darwin LDFLAGS: -framework Foundation -framework MultipeerConnectivity #include #include "mc-driver.h" */ import "C" import ( "fmt" "os" "unsafe" "go.uber.org/zap" proximity "berty.tech/weshnet/v2/pkg/proximitytransport" ) var ( Logger *zap.Logger ProtocolName string ) //export MCHandleFoundPeer func MCHandleFoundPeer(remotePID *C.char) int { goPID := C.GoString(remotePID) proximity.TransportMapMutex.RLock() t, ok := proximity.TransportMap[ProtocolName] proximity.TransportMapMutex.RUnlock() if !ok { return 0 } if t.HandleFoundPeer(goPID) { return 1 } return 0 } //export MCHandleLostPeer func MCHandleLostPeer(remotePID *C.char) { goPID := C.GoString(remotePID) proximity.TransportMapMutex.RLock() t, ok := proximity.TransportMap[ProtocolName] proximity.TransportMapMutex.RUnlock() if !ok { return } t.HandleLostPeer(goPID) } //export MCReceiveFromPeer func MCReceiveFromPeer(remotePID *C.char, payload unsafe.Pointer, length C.int) { goPID := C.GoString(remotePID) goPayload := C.GoBytes(payload, length) proximity.TransportMapMutex.RLock() t, ok := proximity.TransportMap[ProtocolName] proximity.TransportMapMutex.RUnlock() if !ok { return } t.ReceiveFromPeer(goPID, goPayload) } //export MCLog func MCLog(level C.enum_level, message *C.char) { //nolint:golint if Logger == nil { fmt.Fprintf(os.Stderr, "logger not found\n") return } goMessage := C.GoString(message) switch level { case C.Debug: Logger.Debug(goMessage) case C.Info: Logger.Info(goMessage) case C.Warn: Logger.Warn(goMessage) case C.Error: Logger.Error(goMessage) } } func Start(localPID string) { cPID := C.CString(localPID) defer C.free(unsafe.Pointer(cPID)) C.StartMCDriver(cPID) } func Stop() { C.StopMCDriver() } func DialPeer(remotePID string) bool { cPID := C.CString(remotePID) defer C.free(unsafe.Pointer(cPID)) return C.DialPeer(cPID) == 1 } func SendToPeer(remotePID string, payload []byte) bool { cPID := C.CString(remotePID) defer C.free(unsafe.Pointer(cPID)) cPayload := C.CBytes(payload) defer C.free(cPayload) return C.SendToPeer(cPID, cPayload, C.int(len(payload))) == 1 } func CloseConnWithPeer(remotePID string) { cPID := C.CString(remotePID) defer C.free(unsafe.Pointer(cPID)) C.CloseConnWithPeer(cPID) } func MCUseExternalLogger() { C.MCUseExternalLogger() } ================================================ FILE: pkg/multipeer-connectivity-driver/driver/mc-driver.h ================================================ // // mc-driver.h // driver // // Created by Rémi BARBERO on 30/03/2020. // Copyright © 2020 Rémi BARBERO. All rights reserved. // #import #import #import "Logger.h" void StartMCDriver(char *localPId); void StopMCDriver(void); int SendToPeer(char *remotePID, void *payload, int length); int DialPeer(char *remotePID); void CloseConnWithPeer(char *remotePID); int BridgeHandleFoundPeer(NSString *remotePID); void BridgeHandleLostPeer(NSString *remotePID); void BridgeReceiveFromPeer(NSString *remotePID, NSData *payload); void MCBridgeLog(enum level level, NSString *message); void MCUseExternalLogger(void); ================================================ FILE: pkg/multipeer-connectivity-driver/driver/mc-driver.m ================================================ // +build darwin,!noproximitytransport #import #import "mc-driver.h" #import "MCManager.h" // This functions are Go functions so they aren't defined here extern int MCHandleFoundPeer(char *); extern void MCHandleLostPeer(char *); extern void MCReceiveFromPeer(char *, void *, unsigned long); extern void MCLog(enum level level, const char *message); int driverStarted = 0; BOOL gMCUseExternalLogger = FALSE; // MCManager must be unique static MCManager *gMCManager = nil; MCManager* getMCManager(NSString *peerID) { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSLog(@"init MCManager"); gMCManager = [[MCManager alloc] init:peerID useExternalLogger:gMCUseExternalLogger]; }); return gMCManager; } void StartMCDriver(char *localPID) { if (!driverStarted) { NSLog(@"StartMCDriver()"); NSString *cPID = [[NSString alloc] initWithUTF8String:localPID]; if (!getMCManager(cPID)) { NSLog(@"StartMCDriver failed"); return ; } [gMCManager startServiceAdvertiser]; [gMCManager startServiceBrowser]; driverStarted = 1; } } void StopMCDriver() { if (driverStarted) { NSLog(@"StopMCDriver()"); [gMCManager stopServiceAdvertiser]; [gMCManager stopServiceBrowser]; [gMCManager closeSessions]; driverStarted = 0; } } int SendToPeer(char *remotePID, void *payload, int length) { if (driverStarted) { NSString *cPID = [[NSString alloc] initWithUTF8String:remotePID]; NSData *cPayload = [[NSData alloc] initWithBytes:payload length:length]; return ([gMCManager sendToPeer:cPID data:cPayload]); } return (0); } int DialPeer(char *remotePID) { NSString *cPID = [[NSString alloc] initWithUTF8String:remotePID]; if (!driverStarted || ![gMCManager getPeer:cPID]) { return (0); } return (1); } // nothing to do because API doesn't provide any functions void CloseConnWithPeer(char *peerID) { } // Use MCBridgeLog to write logs to the external logger void MCUseExternalLogger(void) { gMCUseExternalLogger = TRUE; } int BridgeHandleFoundPeer(NSString *remotePID) { char *cPID = (char *)[remotePID UTF8String]; if (MCHandleFoundPeer(cPID)) { return (1); } return (0); } void BridgeHandleLostPeer(NSString *remotePID) { char *cPID = (char *)[remotePID UTF8String]; MCHandleLostPeer(cPID); } void BridgeReceiveFromPeer(NSString *remotePID, NSData *payload) { char *cPID = (char *)[remotePID UTF8String]; char *cPayload = (char *)[payload bytes]; int length = (int)[payload length]; MCReceiveFromPeer(cPID, cPayload, length); } // Write logs to the external logger void MCBridgeLog(enum level level, NSString *message) { char *cMessage = (char *)[message UTF8String]; MCLog(level, cMessage); } ================================================ FILE: pkg/multipeer-connectivity-driver/example_test.go ================================================ package mc_test ================================================ FILE: pkg/multipeer-connectivity-driver/init.go ================================================ package mc import ( ma "github.com/multiformats/go-multiaddr" ) // Add MC to the list of libp2p's multiaddr protocols // FIXME: remove this init func init() { // nolint:gochecknoinits err := ma.AddProtocol(newProtocol()) if err != nil { panic(err) } } ================================================ FILE: pkg/multipeer-connectivity-driver/multiaddr.go ================================================ package mc import ( peer "github.com/libp2p/go-libp2p/core/peer" ma "github.com/multiformats/go-multiaddr" ) func newProtocol() ma.Protocol { transcoderMC := ma.NewTranscoderFromFunctions(mcStB, mcBtS, mcVal) return ma.Protocol{ Name: ProtocolName, Code: ProtocolCode, VCode: ma.CodeToVarint(ProtocolCode), Size: -1, Path: false, Transcoder: transcoderMC, } } func mcStB(s string) ([]byte, error) { _, err := peer.Decode(s) if err != nil { return nil, err } return []byte(s), nil } func mcBtS(b []byte) (string, error) { _, err := peer.Decode(string(b)) if err != nil { return "", err } return string(b), nil } func mcVal(b []byte) error { _, err := peer.Decode(string(b)) return err } ================================================ FILE: pkg/netmanager/connectivity.go ================================================ package netmanager import ( "fmt" "strings" ) type ConnectivityState int const ( ConnectivityStateUnknown ConnectivityState = iota ConnectivityStateOff ConnectivityStateOn ) func (cs ConnectivityState) String() string { switch cs { case ConnectivityStateUnknown: return "unknown" case ConnectivityStateOff: return "off" case ConnectivityStateOn: return "on" default: return "error" } } func ParseConnectivityState(s string) (ConnectivityState, error) { switch strings.ToLower(s) { case "unknown": return ConnectivityStateUnknown, nil case "off": return ConnectivityStateOff, nil case "on": return ConnectivityStateOn, nil default: return ConnectivityStateUnknown, fmt.Errorf("invalid connectivity state (unknown/off/on): %q", s) } } type ConnectivityNetType int const ( ConnectivityNetUnknown ConnectivityNetType = iota ConnectivityNetNone ConnectivityNetWifi ConnectivityNetEthernet ConnectivityNetCellular ) func (cnt ConnectivityNetType) String() string { switch cnt { case ConnectivityNetUnknown: return "unknown" case ConnectivityNetNone: return "none" case ConnectivityNetWifi: return "wifi" case ConnectivityNetEthernet: return "ethernet" case ConnectivityNetCellular: return "cellular" default: return "error" } } func ParseConnectivityNetType(s string) (ConnectivityNetType, error) { switch strings.ToLower(s) { case "unknown": return ConnectivityNetUnknown, nil case "none": return ConnectivityNetNone, nil case "wifi": return ConnectivityNetWifi, nil case "ethernet": return ConnectivityNetEthernet, nil case "cellular": return ConnectivityNetCellular, nil default: return ConnectivityNetUnknown, fmt.Errorf("invalid connectivity net type (unknown/none/wifi/ethernet/cellular): %q", s) } } type ConnectivityCellularType int const ( ConnectivityCellularUnknown ConnectivityCellularType = iota ConnectivityCellularNone ConnectivityCellular2G ConnectivityCellular3G ConnectivityCellular4G ConnectivityCellular5G ) func (cct ConnectivityCellularType) String() string { switch cct { case ConnectivityCellularUnknown: return "unknown" case ConnectivityCellularNone: return "none" case ConnectivityCellular2G: return "2G" case ConnectivityCellular3G: return "3G" case ConnectivityCellular4G: return "4G" case ConnectivityCellular5G: return "5G" default: return "error" } } func ParseConnectivityCellularType(s string) (ConnectivityCellularType, error) { switch strings.ToLower(s) { case "unknown": return ConnectivityCellularUnknown, nil case "none": return ConnectivityCellularNone, nil case "2g": return ConnectivityCellular2G, nil case "3g": return ConnectivityCellular3G, nil case "4g": return ConnectivityCellular4G, nil case "5g": return ConnectivityCellular5G, nil default: return ConnectivityCellularUnknown, fmt.Errorf("invalid connectivity cellular type (unknown/none/2g/3g/4g/5g): %q", s) } } type ConnectivityInfo struct { // False when the device is not connected to a network. State ConnectivityState // True when the device is connected to a metered network. Metering ConnectivityState // True when the device is connected to a bluetooth network. Bluetooth ConnectivityState // The type of the network the device is connected to: wifi/ethernet/cellular. NetType ConnectivityNetType // If the device is connected to a cellular network: // The type of the cellular network the device is connected to: 2G/3G/4G/5G. CellularType ConnectivityCellularType } func (ci ConnectivityInfo) String() string { return fmt.Sprint("ConnectivityInfo{ ", "State: ", ci.State.String(), ", ", "Metering: ", ci.Metering.String(), ", ", "Bluetooth: ", ci.Bluetooth.String(), ", ", "NetType: ", ci.NetType.String(), ", ", "CellularType: ", ci.CellularType.String(), " }") } ================================================ FILE: pkg/netmanager/netmanager.go ================================================ package netmanager import ( "context" "sync" "berty.tech/weshnet/v2/internal/notify" ) type NetManager struct { currentState ConnectivityInfo locker *sync.RWMutex notify *notify.Notify } type EventType uint const ( ConnectivityStateChanged EventType = 1 << iota ConnectivityMeteringChanged ConnectivityBluetoothChanged ConnectivityNetTypeChanged ConnectivityCellularTypeChanged ConnectivityChanged = 0 | ConnectivityStateChanged | ConnectivityMeteringChanged | ConnectivityBluetoothChanged | ConnectivityNetTypeChanged | ConnectivityCellularTypeChanged ) func (t EventType) Has(other EventType) bool { return (t & other) == other } func NewNetManager(initialState ConnectivityInfo) *NetManager { var locker sync.RWMutex return &NetManager{ currentState: initialState, locker: &locker, notify: notify.New(&locker), } } // UpdateState update the current state of the Manager func (m *NetManager) UpdateState(state ConnectivityInfo) { m.locker.Lock() if m.currentState != state { m.currentState = state m.notify.Broadcast() } m.locker.Unlock() } // WaitForStateChange waits until the currentState changes from sourceState or ctx expires. // The eventType argument allow you to filter out the event you want to wait for. // A true value is returned in former case and false in latter. // The EventType is also returned to know which events has been triggered. func (m *NetManager) WaitForStateChange(ctx context.Context, sourceState *ConnectivityInfo, eventType EventType) (bool, EventType) { if ctx.Err() != nil { return false, 0 } m.locker.Lock() defer m.locker.Unlock() var currentEventType EventType ok := true for ok { currentEventType = 0 if sourceState.State != m.currentState.State { currentEventType |= ConnectivityStateChanged } if sourceState.Metering != m.currentState.Metering { currentEventType |= ConnectivityMeteringChanged } if sourceState.Bluetooth != m.currentState.Bluetooth { currentEventType |= ConnectivityBluetoothChanged } if sourceState.NetType != m.currentState.NetType { currentEventType |= ConnectivityNetTypeChanged } if sourceState.CellularType != m.currentState.CellularType { currentEventType |= ConnectivityCellularTypeChanged } if (eventType & currentEventType) != 0 { break } // wait until state has been changed or context has been cancel ok = m.notify.Wait(ctx) } return ok, currentEventType } // GetCurrentState return the current state of the Manager func (m *NetManager) GetCurrentState() (state ConnectivityInfo) { m.locker.RLock() state = m.currentState m.locker.RUnlock() return } ================================================ FILE: pkg/netmanager/netmanager_noop.go ================================================ package netmanager func NewNoopNetManager() *NetManager { return NewNetManager(ConnectivityInfo{ State: ConnectivityStateOn, NetType: ConnectivityNetWifi, }) } ================================================ FILE: pkg/netmanager/netmanager_test.go ================================================ package netmanager import ( "context" "testing" "github.com/stretchr/testify/require" ) func TestNewNetManager(t *testing.T) { initial := ConnectivityInfo{ State: ConnectivityStateOn, NetType: ConnectivityNetWifi, Bluetooth: ConnectivityStateOn, } netmanager := NewNetManager(initial) require.Equal(t, initial, netmanager.GetCurrentState()) initial.State = ConnectivityStateOff require.NotEqual(t, initial, netmanager.GetCurrentState()) } func TestNetManagerSingleUpdate(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() a := ConnectivityInfo{ State: ConnectivityStateOn, } state := ConnectivityInfo{} netmanager := NewNetManager(state) netmanager.UpdateState(a) require.Equal(t, a, netmanager.GetCurrentState()) ok, eventType := netmanager.WaitForStateChange(ctx, &state, ConnectivityChanged) require.Equal(t, a, netmanager.GetCurrentState()) require.True(t, ok) require.Equal(t, ConnectivityStateChanged, eventType) } func TestNetManagerDoubleUpdate(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() a := ConnectivityInfo{ State: ConnectivityStateOn, } b := ConnectivityInfo{ State: ConnectivityStateOff, } state := ConnectivityInfo{} netmanager := NewNetManager(state) netmanager.UpdateState(a) require.Equal(t, a, netmanager.GetCurrentState()) netmanager.UpdateState(b) require.Equal(t, b, netmanager.GetCurrentState()) ok, eventType := netmanager.WaitForStateChange(ctx, &state, ConnectivityChanged) require.Equal(t, b, netmanager.GetCurrentState()) require.True(t, ok) require.Equal(t, ConnectivityStateChanged, eventType) } func TestNetManagerFilterUpdate(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() a := ConnectivityInfo{ State: ConnectivityStateOff, } b := ConnectivityInfo{ State: ConnectivityStateOn, NetType: ConnectivityNetCellular, CellularType: ConnectivityCellular3G, } state := ConnectivityInfo{} netmanager := NewNetManager(state) netmanager.UpdateState(a) require.Equal(t, a, netmanager.GetCurrentState()) netmanager.UpdateState(b) require.Equal(t, b, netmanager.GetCurrentState()) ok, eventType := netmanager.WaitForStateChange(ctx, &state, ConnectivityCellularTypeChanged) require.Equal(t, b, netmanager.GetCurrentState()) require.True(t, ok) require.Equal(t, ConnectivityStateChanged|ConnectivityNetTypeChanged|ConnectivityCellularTypeChanged, eventType) } ================================================ FILE: pkg/outofstoremessage/outofstoremessage_test.go ================================================ package outofstoremessage import ( "context" "testing" "time" "github.com/ipfs/go-cid" "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" "berty.tech/weshnet/v2" "berty.tech/weshnet/v2/pkg/protocoltypes" "berty.tech/weshnet/v2/pkg/secretstore" "berty.tech/weshnet/v2/pkg/testutil" ) func Test_sealPushMessage_OutOfStoreReceive(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() tp, cancel := weshnet.NewTestingProtocol(ctx, t, &weshnet.TestingOpts{}, nil) defer cancel() g, _, err := weshnet.NewGroupMultiMember() require.NoError(t, err) s := tp.Service gPK, err := g.GetPubKey() require.NoError(t, err) _, err = s.MultiMemberGroupJoin(ctx, &protocoltypes.MultiMemberGroupJoin_Request{Group: g}) require.NoError(t, err) gPKRaw, err := gPK.Raw() require.NoError(t, err) _, err = s.ActivateGroup(ctx, &protocoltypes.ActivateGroup_Request{GroupPk: gPKRaw}) require.NoError(t, err) gc, err := s.(weshnet.ServiceMethods).GetContextGroupForID(g.PublicKey) require.NoError(t, err) otherSecretStore, cancel := createVirtualOtherPeerSecrets(t, ctx, gc) defer cancel() testPayload := []byte("test payload") envBytes, err := otherSecretStore.SealEnvelope(ctx, g, testPayload) require.NoError(t, err) env, headers, err := otherSecretStore.OpenEnvelopeHeaders(envBytes, g) require.NoError(t, err) oosMsgEnv, err := otherSecretStore.SealOutOfStoreMessageEnvelope(cid.Undef, env, headers, g) require.NoError(t, err) oosMsgEnvBytes, err := proto.Marshal(oosMsgEnv) require.NoError(t, err) outOfStoreMessage, group, clearPayload, alreadyDecrypted, err := gc.SecretStore().OpenOutOfStoreMessage(ctx, oosMsgEnvBytes) require.NoError(t, err) require.Equal(t, g.PublicKey, group.PublicKey) require.Equal(t, g.Secret, group.Secret) require.Equal(t, g.SecretSig, group.SecretSig) require.Equal(t, g.GroupType, group.GroupType) require.Equal(t, g.SignPub, group.SignPub) require.Equal(t, g.LinkKey, group.LinkKey) require.Equal(t, g.LinkKeySig, group.LinkKeySig) require.Equal(t, []byte("test payload"), clearPayload) require.False(t, alreadyDecrypted) require.Equal(t, headers.Counter, outOfStoreMessage.Counter) require.Equal(t, headers.DevicePk, outOfStoreMessage.DevicePk) require.Equal(t, headers.Sig, outOfStoreMessage.Sig) require.Equal(t, env.Message, outOfStoreMessage.EncryptedPayload) } func Test_OutOfStoreMessageFlow(t *testing.T) { message := []byte("test message") ctx, cancel := context.WithCancel(context.Background()) defer cancel() logger, cleanup := testutil.Logger(t) defer cleanup() tp, cancel := weshnet.NewTestingProtocol(ctx, t, &weshnet.TestingOpts{Logger: logger}, nil) defer cancel() g, _, err := weshnet.NewGroupMultiMember() require.NoError(t, err) s := tp.Service gPK, err := g.GetPubKey() require.NoError(t, err) _, err = s.MultiMemberGroupJoin(ctx, &protocoltypes.MultiMemberGroupJoin_Request{Group: g}) require.NoError(t, err) gPKRaw, err := gPK.Raw() require.NoError(t, err) _, err = s.ActivateGroup(ctx, &protocoltypes.ActivateGroup_Request{GroupPk: gPKRaw}) require.NoError(t, err) // send a message sendReply, err := s.AppMessageSend(ctx, &protocoltypes.AppMessageSend_Request{ GroupPk: gPKRaw, Payload: message, }) require.NoError(t, err) time.Sleep(100 * time.Millisecond) // craft an out of store message craftReply, err := s.OutOfStoreSeal(ctx, &protocoltypes.OutOfStoreSeal_Request{ Cid: sendReply.Cid, GroupPublicKey: gPKRaw, }) require.NoError(t, err) // verify the out of store message openReply, err := s.OutOfStoreReceive(ctx, &protocoltypes.OutOfStoreReceive_Request{ Payload: craftReply.Encrypted, }) require.NoError(t, err) encryptedMessage := &protocoltypes.EncryptedMessage{} err = proto.Unmarshal(openReply.Cleartext, encryptedMessage) require.NoError(t, err) require.Equal(t, message, encryptedMessage.Plaintext) } func createVirtualOtherPeerSecrets(t testing.TB, ctx context.Context, gc *weshnet.GroupContext) (secretstore.SecretStore, func()) { secretStore, err := secretstore.NewInMemSecretStore(nil) require.NoError(t, err) cleanup := func() { _ = secretStore.Close() } // Manually adding another member to the group otherMD, err := secretStore.GetOwnMemberDeviceForGroup(gc.Group()) require.NoError(t, err) _, err = weshnet.MetadataStoreAddDeviceToGroup(ctx, gc.MetadataStore(), gc.Group(), otherMD) require.NoError(t, err) memberDevice, err := gc.SecretStore().GetOwnMemberDeviceForGroup(gc.Group()) require.NoError(t, err) ds, err := secretStore.GetShareableChainKey(ctx, gc.Group(), memberDevice.Member()) require.NoError(t, err) _, err = weshnet.MetadataStoreSendSecret(ctx, gc.MetadataStore(), gc.Group(), otherMD, memberDevice.Member(), ds) require.NoError(t, err) time.Sleep(time.Millisecond * 200) return secretStore, cleanup } ================================================ FILE: pkg/outofstoremessage/service_outofstoremessage.go ================================================ package outofstoremessage import ( "context" "fmt" "io" "time" ds "github.com/ipfs/go-datastore" ds_sync "github.com/ipfs/go-datastore/sync" coreiface "github.com/ipfs/kubo/core/coreiface" "go.uber.org/zap" "google.golang.org/grpc" "berty.tech/weshnet/v2" "berty.tech/weshnet/v2/pkg/errcode" "berty.tech/weshnet/v2/pkg/grpcutil" "berty.tech/weshnet/v2/pkg/outofstoremessagetypes" "berty.tech/weshnet/v2/pkg/protocoltypes" "berty.tech/weshnet/v2/pkg/secretstore" ) type OOSMService interface { outofstoremessagetypes.OutOfStoreMessageServiceServer } var _ OOSMService = (*oosmService)(nil) type oosmService struct { logger *zap.Logger rootDatastore ds.Datastore secretStore secretstore.SecretStore outofstoremessagetypes.UnimplementedOutOfStoreMessageServiceServer } type OOSMServiceClient interface { outofstoremessagetypes.OutOfStoreMessageServiceClient io.Closer } type oosmServiceClient struct { OOSMServiceClient service OOSMService server *grpc.Server } type OOSMOption func(*oosmService) error // NewOutOfStoreMessageServiceClient creates a new Wesh protocol service and returns a gRPC // ServiceClient which uses a direct in-memory connection. When finished, you must call Close(). // This opens or creates a Wesh account where the datastore location is specified by the path argument. // The service will not start any network stuff, it will only use the filesystem to store or get data. func NewOutOfStoreMessageServiceClient(opts ...OOSMOption) (OOSMServiceClient, error) { svc, err := NewOutOfStoreMessageService(opts...) if err != nil { return nil, err } s := grpc.NewServer() ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) defer cancel() c, err := newClientFromService(ctx, s, svc) if err != nil { return nil, fmt.Errorf("unable to create client from server: %w", err) } return &oosmServiceClient{ OOSMServiceClient: c, server: s, service: svc, }, nil } type oosmClient struct { outofstoremessagetypes.OutOfStoreMessageServiceClient l *grpcutil.BufListener cc *grpc.ClientConn } func (c *oosmClient) Close() error { err := c.cc.Close() _ = c.l.Close() return err } func newClientFromService(ctx context.Context, s *grpc.Server, svc OOSMService, opts ...grpc.DialOption) (OOSMServiceClient, error) { bl := grpcutil.NewBufListener(weshnet.ClientBufferSize) cc, err := bl.NewClientConn(ctx, opts...) if err != nil { return nil, err } outofstoremessagetypes.RegisterOutOfStoreMessageServiceServer(s, svc) go func() { // we dont need to log the error _ = s.Serve(bl) }() return &oosmClient{ OutOfStoreMessageServiceClient: outofstoremessagetypes.NewOutOfStoreMessageServiceClient(cc), cc: cc, l: bl, }, nil } func NewOutOfStoreMessageService(opts ...OOSMOption) (OOSMService, error) { svc := &oosmService{} withDefaultOpts := make([]OOSMOption, len(opts)) copy(withDefaultOpts, opts) withDefaultOpts = append(withDefaultOpts, WithFallbackDefaults) for _, opt := range withDefaultOpts { if err := opt(svc); err != nil { return nil, err } } return svc, nil } func (s *oosmService) Close() error { return nil } func (s *oosmService) Status() (weshnet.Status, error) { return weshnet.Status{}, nil } func (s *oosmService) IpfsCoreAPI() coreiface.CoreAPI { return nil } func (s *oosmService) OutOfStoreReceive(ctx context.Context, request *protocoltypes.OutOfStoreReceive_Request) (*protocoltypes.OutOfStoreReceive_Reply, error) { outOfStoreMessage, group, clearPayload, alreadyDecrypted, err := s.secretStore.OpenOutOfStoreMessage(ctx, request.Payload) if err != nil { return nil, errcode.ErrCode_ErrCryptoDecrypt.Wrap(err) } return &protocoltypes.OutOfStoreReceive_Reply{ Message: outOfStoreMessage, Cleartext: clearPayload, GroupPublicKey: group.PublicKey, AlreadyReceived: alreadyDecrypted, }, nil } // FallBackOption is a structure that permit to fallback to a default option if the option is not set. type FallBackOption struct { fallback func(s *oosmService) bool opt OOSMOption } // WithLogger set the given logger. var WithLogger = func(l *zap.Logger) OOSMOption { return func(s *oosmService) error { s.logger = l return nil } } // WithDefaultLogger init a noop logger. var WithDefaultLogger OOSMOption = func(s *oosmService) error { s.logger = zap.NewNop() return nil } var fallbackLogger = FallBackOption{ fallback: func(s *oosmService) bool { return s.logger == nil }, opt: WithDefaultLogger, } // WithFallbackLogger set the logger if no logger is set. var WithFallbackLogger OOSMOption = func(s *oosmService) error { if fallbackLogger.fallback(s) { return fallbackLogger.opt(s) } return nil } // WithRootDatastore set the root datastore. var WithRootDatastore = func(ds ds.Datastore) OOSMOption { return func(s *oosmService) error { s.rootDatastore = ds return nil } } // WithDefaultRootDatastore init a in-memory datastore. var WithDefaultRootDatastore OOSMOption = func(s *oosmService) error { s.rootDatastore = ds_sync.MutexWrap(ds.NewMapDatastore()) return nil } var fallbackRootDatastore = FallBackOption{ fallback: func(s *oosmService) bool { return s.rootDatastore == nil }, opt: WithDefaultRootDatastore, } // WithFallbackRootDatastore set the root datastore if no root datastore is set. var WithFallbackRootDatastore OOSMOption = func(s *oosmService) error { if fallbackRootDatastore.fallback(s) { return fallbackRootDatastore.opt(s) } return nil } // WithSecretStore set the secret store. var WithSecretStore = func(ss secretstore.SecretStore) OOSMOption { return func(s *oosmService) error { s.secretStore = ss return nil } } // WithDefaultSecretStore init a new secret store. // Call WithRootDatastore before this option if you want to use your datastore. // Call WithLogger before this option if you want to use your logger. var WithDefaultSecretStore OOSMOption = func(s *oosmService) error { // dependency if err := WithFallbackRootDatastore(s); err != nil { return err } if err := WithFallbackLogger(s); err != nil { return err } var err error s.secretStore, err = secretstore.NewSecretStore(s.rootDatastore, &secretstore.NewSecretStoreOptions{ Logger: s.logger, }) return err } var fallbackSecretStore = FallBackOption{ fallback: func(s *oosmService) bool { return s.secretStore == nil }, opt: WithDefaultSecretStore, } // WithFallbackSecretStore set the secret store if no secret store is set. // Call WithRootDatastore before this option if you want to use your datastore if a new secret store is created. // Call WithLogger before this option if you want to use your logger if a new secret store is created. var WithFallbackSecretStore OOSMOption = func(s *oosmService) error { if fallbackSecretStore.fallback(s) { return fallbackSecretStore.opt(s) } return nil } var defaults = []FallBackOption{ fallbackLogger, fallbackRootDatastore, fallbackSecretStore, } // WithFallbackDefaults set the default options if no option is set. var WithFallbackDefaults OOSMOption = func(s *oosmService) error { for _, def := range defaults { if !def.fallback(s) { continue } if err := def.opt(s); err != nil { return err } } return nil } ================================================ FILE: pkg/outofstoremessagetypes/outofstoremessage.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.34.2 // protoc (unknown) // source: outofstoremessagetypes/outofstoremessage.proto package outofstoremessagetypes import ( protocoltypes "berty.tech/weshnet/v2/pkg/protocoltypes" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) var File_outofstoremessagetypes_outofstoremessage_proto protoreflect.FileDescriptor var file_outofstoremessagetypes_outofstoremessage_proto_rawDesc = []byte{ 0x0a, 0x2e, 0x6f, 0x75, 0x74, 0x6f, 0x66, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x6f, 0x75, 0x74, 0x6f, 0x66, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1c, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x6f, 0x75, 0x74, 0x6f, 0x66, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x13, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, 0x8d, 0x01, 0x0a, 0x18, 0x4f, 0x75, 0x74, 0x4f, 0x66, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x71, 0x0a, 0x11, 0x4f, 0x75, 0x74, 0x4f, 0x66, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x12, 0x2e, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x4f, 0x66, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x4f, 0x66, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x42, 0x32, 0x5a, 0x30, 0x62, 0x65, 0x72, 0x74, 0x79, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x2f, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2f, 0x76, 0x32, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x6f, 0x75, 0x74, 0x6f, 0x66, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x74, 0x79, 0x70, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var file_outofstoremessagetypes_outofstoremessage_proto_goTypes = []any{ (*protocoltypes.OutOfStoreReceive_Request)(nil), // 0: weshnet.protocol.v1.OutOfStoreReceive.Request (*protocoltypes.OutOfStoreReceive_Reply)(nil), // 1: weshnet.protocol.v1.OutOfStoreReceive.Reply } var file_outofstoremessagetypes_outofstoremessage_proto_depIdxs = []int32{ 0, // 0: weshnet.outofstoremessage.v1.OutOfStoreMessageService.OutOfStoreReceive:input_type -> weshnet.protocol.v1.OutOfStoreReceive.Request 1, // 1: weshnet.outofstoremessage.v1.OutOfStoreMessageService.OutOfStoreReceive:output_type -> weshnet.protocol.v1.OutOfStoreReceive.Reply 1, // [1:2] is the sub-list for method output_type 0, // [0:1] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_outofstoremessagetypes_outofstoremessage_proto_init() } func file_outofstoremessagetypes_outofstoremessage_proto_init() { if File_outofstoremessagetypes_outofstoremessage_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_outofstoremessagetypes_outofstoremessage_proto_rawDesc, NumEnums: 0, NumMessages: 0, NumExtensions: 0, NumServices: 1, }, GoTypes: file_outofstoremessagetypes_outofstoremessage_proto_goTypes, DependencyIndexes: file_outofstoremessagetypes_outofstoremessage_proto_depIdxs, }.Build() File_outofstoremessagetypes_outofstoremessage_proto = out.File file_outofstoremessagetypes_outofstoremessage_proto_rawDesc = nil file_outofstoremessagetypes_outofstoremessage_proto_goTypes = nil file_outofstoremessagetypes_outofstoremessage_proto_depIdxs = nil } ================================================ FILE: pkg/outofstoremessagetypes/outofstoremessage.pb.gw.go ================================================ // Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. // source: outofstoremessagetypes/outofstoremessage.proto /* Package outofstoremessagetypes is a reverse proxy. It translates gRPC into RESTful JSON APIs. */ package outofstoremessagetypes import ( "context" "io" "net/http" "berty.tech/weshnet/v2/pkg/protocoltypes" "github.com/golang/protobuf/descriptor" "github.com/golang/protobuf/proto" "github.com/grpc-ecosystem/grpc-gateway/runtime" "github.com/grpc-ecosystem/grpc-gateway/utilities" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/grpclog" "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" ) // Suppress "imported and not used" errors var _ codes.Code var _ io.Reader var _ status.Status var _ = runtime.String var _ = utilities.NewDoubleArray var _ = descriptor.ForMessage var _ = metadata.Join func request_OutOfStoreMessageService_OutOfStoreReceive_0(ctx context.Context, marshaler runtime.Marshaler, client OutOfStoreMessageServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq protocoltypes.OutOfStoreReceive_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := client.OutOfStoreReceive(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } func 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) { var protoReq protocoltypes.OutOfStoreReceive_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := server.OutOfStoreReceive(ctx, &protoReq) return msg, metadata, err } // RegisterOutOfStoreMessageServiceHandlerServer registers the http handlers for service OutOfStoreMessageService to "mux". // UnaryRPC :call OutOfStoreMessageServiceServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. // Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterOutOfStoreMessageServiceHandlerFromEndpoint instead. func RegisterOutOfStoreMessageServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server OutOfStoreMessageServiceServer) error { mux.Handle("POST", pattern_OutOfStoreMessageService_OutOfStoreReceive_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := local_request_OutOfStoreMessageService_OutOfStoreReceive_0(rctx, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_OutOfStoreMessageService_OutOfStoreReceive_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) return nil } // RegisterOutOfStoreMessageServiceHandlerFromEndpoint is same as RegisterOutOfStoreMessageServiceHandler but // automatically dials to "endpoint" and closes the connection when "ctx" gets done. func RegisterOutOfStoreMessageServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { conn, err := grpc.Dial(endpoint, opts...) if err != nil { return err } defer func() { if err != nil { if cerr := conn.Close(); cerr != nil { grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) } return } go func() { <-ctx.Done() if cerr := conn.Close(); cerr != nil { grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) } }() }() return RegisterOutOfStoreMessageServiceHandler(ctx, mux, conn) } // RegisterOutOfStoreMessageServiceHandler registers the http handlers for service OutOfStoreMessageService to "mux". // The handlers forward requests to the grpc endpoint over "conn". func RegisterOutOfStoreMessageServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { return RegisterOutOfStoreMessageServiceHandlerClient(ctx, mux, NewOutOfStoreMessageServiceClient(conn)) } // RegisterOutOfStoreMessageServiceHandlerClient registers the http handlers for service OutOfStoreMessageService // to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "OutOfStoreMessageServiceClient". // Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "OutOfStoreMessageServiceClient" // doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in // "OutOfStoreMessageServiceClient" to call the correct interceptors. func RegisterOutOfStoreMessageServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client OutOfStoreMessageServiceClient) error { mux.Handle("POST", pattern_OutOfStoreMessageService_OutOfStoreReceive_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_OutOfStoreMessageService_OutOfStoreReceive_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_OutOfStoreMessageService_OutOfStoreReceive_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) return nil } var ( pattern_OutOfStoreMessageService_OutOfStoreReceive_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.outofstoremessage.v1.OutOfStoreMessageService", "OutOfStoreReceive"}, "", runtime.AssumeColonVerbOpt(true))) ) var ( forward_OutOfStoreMessageService_OutOfStoreReceive_0 = runtime.ForwardResponseMessage ) ================================================ FILE: pkg/outofstoremessagetypes/outofstoremessage_grpc.pb.go ================================================ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.5.1 // - protoc (unknown) // source: outofstoremessagetypes/outofstoremessage.proto package outofstoremessagetypes import ( protocoltypes "berty.tech/weshnet/v2/pkg/protocoltypes" context "context" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" ) // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. // Requires gRPC-Go v1.64.0 or later. const _ = grpc.SupportPackageIsVersion9 const ( OutOfStoreMessageService_OutOfStoreReceive_FullMethodName = "/weshnet.outofstoremessage.v1.OutOfStoreMessageService/OutOfStoreReceive" ) // OutOfStoreMessageServiceClient is the client API for OutOfStoreMessageService service. // // 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. // // OutOfStoreMessageService is the service used to open out-of-store messages (e.g. push notifications) // It is used to open messages with a lightweight protocol service for mobile backgroup processes. type OutOfStoreMessageServiceClient interface { // OutOfStoreReceive parses a payload received outside a synchronized store OutOfStoreReceive(ctx context.Context, in *protocoltypes.OutOfStoreReceive_Request, opts ...grpc.CallOption) (*protocoltypes.OutOfStoreReceive_Reply, error) } type outOfStoreMessageServiceClient struct { cc grpc.ClientConnInterface } func NewOutOfStoreMessageServiceClient(cc grpc.ClientConnInterface) OutOfStoreMessageServiceClient { return &outOfStoreMessageServiceClient{cc} } func (c *outOfStoreMessageServiceClient) OutOfStoreReceive(ctx context.Context, in *protocoltypes.OutOfStoreReceive_Request, opts ...grpc.CallOption) (*protocoltypes.OutOfStoreReceive_Reply, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(protocoltypes.OutOfStoreReceive_Reply) err := c.cc.Invoke(ctx, OutOfStoreMessageService_OutOfStoreReceive_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } // OutOfStoreMessageServiceServer is the server API for OutOfStoreMessageService service. // All implementations must embed UnimplementedOutOfStoreMessageServiceServer // for forward compatibility. // // OutOfStoreMessageService is the service used to open out-of-store messages (e.g. push notifications) // It is used to open messages with a lightweight protocol service for mobile backgroup processes. type OutOfStoreMessageServiceServer interface { // OutOfStoreReceive parses a payload received outside a synchronized store OutOfStoreReceive(context.Context, *protocoltypes.OutOfStoreReceive_Request) (*protocoltypes.OutOfStoreReceive_Reply, error) mustEmbedUnimplementedOutOfStoreMessageServiceServer() } // UnimplementedOutOfStoreMessageServiceServer must be embedded to have // forward compatible implementations. // // NOTE: this should be embedded by value instead of pointer to avoid a nil // pointer dereference when methods are called. type UnimplementedOutOfStoreMessageServiceServer struct{} func (UnimplementedOutOfStoreMessageServiceServer) OutOfStoreReceive(context.Context, *protocoltypes.OutOfStoreReceive_Request) (*protocoltypes.OutOfStoreReceive_Reply, error) { return nil, status.Errorf(codes.Unimplemented, "method OutOfStoreReceive not implemented") } func (UnimplementedOutOfStoreMessageServiceServer) mustEmbedUnimplementedOutOfStoreMessageServiceServer() { } func (UnimplementedOutOfStoreMessageServiceServer) testEmbeddedByValue() {} // UnsafeOutOfStoreMessageServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to OutOfStoreMessageServiceServer will // result in compilation errors. type UnsafeOutOfStoreMessageServiceServer interface { mustEmbedUnimplementedOutOfStoreMessageServiceServer() } func RegisterOutOfStoreMessageServiceServer(s grpc.ServiceRegistrar, srv OutOfStoreMessageServiceServer) { // If the following call pancis, it indicates UnimplementedOutOfStoreMessageServiceServer was // embedded by pointer and is nil. This will cause panics if an // unimplemented method is ever invoked, so we test this at initialization // time to prevent it from happening at runtime later due to I/O. if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { t.testEmbeddedByValue() } s.RegisterService(&OutOfStoreMessageService_ServiceDesc, srv) } func _OutOfStoreMessageService_OutOfStoreReceive_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(protocoltypes.OutOfStoreReceive_Request) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(OutOfStoreMessageServiceServer).OutOfStoreReceive(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: OutOfStoreMessageService_OutOfStoreReceive_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(OutOfStoreMessageServiceServer).OutOfStoreReceive(ctx, req.(*protocoltypes.OutOfStoreReceive_Request)) } return interceptor(ctx, in, info, handler) } // OutOfStoreMessageService_ServiceDesc is the grpc.ServiceDesc for OutOfStoreMessageService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var OutOfStoreMessageService_ServiceDesc = grpc.ServiceDesc{ ServiceName: "weshnet.outofstoremessage.v1.OutOfStoreMessageService", HandlerType: (*OutOfStoreMessageServiceServer)(nil), Methods: []grpc.MethodDesc{ { MethodName: "OutOfStoreReceive", Handler: _OutOfStoreMessageService_OutOfStoreReceive_Handler, }, }, Streams: []grpc.StreamDesc{}, Metadata: "outofstoremessagetypes/outofstoremessage.proto", } ================================================ FILE: pkg/protocoltypes/contact.go ================================================ package protocoltypes import ( "fmt" "github.com/libp2p/go-libp2p/core/crypto" "berty.tech/weshnet/v2/pkg/errcode" ) const RendezvousSeedLength = 32 type ShareableContactOptions uint64 const ( shareableContactOptionsUndefined = iota ShareableContactOptionsAllowMissingRDVSeed ShareableContactOptionsAllowMissingPK ) var _ = shareableContactOptionsUndefined func (m *ShareableContact) CheckFormat(options ...ShareableContactOptions) error { var ( optionMissingPKAllowed = false optionMissingRDVSeedAllowed = false ) for _, o := range options { if o == ShareableContactOptionsAllowMissingPK { optionMissingPKAllowed = true } if o == ShareableContactOptionsAllowMissingRDVSeed { optionMissingRDVSeedAllowed = true } } if l := len(m.PublicRendezvousSeed); l != RendezvousSeedLength { if !(l == 0 && optionMissingRDVSeedAllowed) { return errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("rendezvous seed length should not be %d", l)) } } if l := len(m.Pk); l == 0 && !optionMissingPKAllowed { return errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("contact public key is missing")) } if l := len(m.Pk); l != 0 { _, err := crypto.UnmarshalEd25519PublicKey(m.Pk) if err != nil { return errcode.ErrCode_ErrDeserialization.Wrap(err) } } return nil } func (m *ShareableContact) IsSamePK(otherPK crypto.PubKey) bool { pk, err := m.GetPubKey() if err != nil { return false } return otherPK.Equals(pk) } func (m *ShareableContact) GetPubKey() (crypto.PubKey, error) { pk, err := crypto.UnmarshalEd25519PublicKey(m.Pk) if err != nil { return nil, errcode.ErrCode_ErrDeserialization.Wrap(err) } return pk, nil } ================================================ FILE: pkg/protocoltypes/doc.go ================================================ // Package protocoltypes contains types and helpers for the Berty Protocol. // This package is generated with Protobuf. // See https://github.com/berty/berty/tree/master/api for more information. package protocoltypes ================================================ FILE: pkg/protocoltypes/events_account.go ================================================ package protocoltypes func (m *AccountGroupJoined) SetDevicePK(pk []byte) { m.DevicePk = pk } func (m *AccountGroupLeft) SetDevicePK(pk []byte) { m.DevicePk = pk } func (m *AccountContactRequestDisabled) SetDevicePK(pk []byte) { m.DevicePk = pk } func (m *AccountContactRequestEnabled) SetDevicePK(pk []byte) { m.DevicePk = pk } func (m *AccountContactRequestReferenceReset) SetDevicePK(pk []byte) { m.DevicePk = pk } func (m *AccountContactRequestOutgoingEnqueued) SetDevicePK(pk []byte) { m.DevicePk = pk } func (m *AccountContactRequestOutgoingSent) SetDevicePK(pk []byte) { m.DevicePk = pk } func (m *AccountContactRequestIncomingReceived) SetDevicePK(pk []byte) { m.DevicePk = pk } func (m *AccountContactRequestIncomingDiscarded) SetDevicePK(pk []byte) { m.DevicePk = pk } func (m *AccountContactRequestIncomingAccepted) SetDevicePK(pk []byte) { m.DevicePk = pk } func (m *AccountContactBlocked) SetDevicePK(pk []byte) { m.DevicePk = pk } func (m *AccountContactUnblocked) SetDevicePK(pk []byte) { m.DevicePk = pk } func (m *AccountContactRequestOutgoingSent) SetContactPK(pk []byte) { m.ContactPk = pk } func (m *AccountContactRequestIncomingDiscarded) SetContactPK(pk []byte) { m.ContactPk = pk } func (m *AccountContactRequestIncomingAccepted) SetContactPK(pk []byte) { m.ContactPk = pk } func (m *AccountContactBlocked) SetContactPK(pk []byte) { m.ContactPk = pk } func (m *AccountContactUnblocked) SetContactPK(pk []byte) { m.ContactPk = pk } func (m *AccountGroupLeft) SetGroupPK(pk []byte) { m.GroupPk = pk } func (m *ContactAliasKeyAdded) SetDevicePK(pk []byte) { m.DevicePk = pk } func (m *MultiMemberGroupAliasResolverAdded) SetDevicePK(pk []byte) { m.DevicePk = pk } func (m *MultiMemberGroupAdminRoleGranted) SetDevicePK(pk []byte) { m.DevicePk = pk } func (m *GroupMetadataPayloadSent) SetDevicePK(pk []byte) { m.DevicePk = pk } func (m *GroupReplicating) SetDevicePK(pk []byte) { m.DevicePk = pk } func (m *AccountVerifiedCredentialRegistered) SetDevicePK(pk []byte) { m.DevicePk = pk } ================================================ FILE: pkg/protocoltypes/example_test.go ================================================ package protocoltypes_test ================================================ FILE: pkg/protocoltypes/group.go ================================================ package protocoltypes import ( crand "crypto/rand" "encoding/hex" "io" "github.com/libp2p/go-libp2p/core/crypto" "golang.org/x/crypto/ed25519" "golang.org/x/crypto/hkdf" "golang.org/x/crypto/sha3" "berty.tech/weshnet/v2/pkg/cryptoutil" "berty.tech/weshnet/v2/pkg/errcode" ) func (m *Group) GetSigningPrivKey() (crypto.PrivKey, error) { if len(m.Secret) == 0 { return nil, errcode.ErrCode_ErrMissingInput } edSK := ed25519.NewKeyFromSeed(m.Secret) sk, _, err := crypto.KeyPairFromStdKey(&edSK) if err != nil { return nil, err } return sk, nil } func (m *Group) GetPubKey() (crypto.PubKey, error) { return crypto.UnmarshalEd25519PublicKey(m.PublicKey) } func (m *Group) GetSigningPubKey() (crypto.PubKey, error) { if len(m.SignPub) != 0 { return crypto.UnmarshalEd25519PublicKey(m.SignPub) } sk, err := m.GetSigningPrivKey() if err != nil { return nil, err } return sk.GetPublic(), nil } func (m *Group) IsValid() error { pk, err := m.GetPubKey() if err != nil { return errcode.ErrCode_ErrDeserialization.Wrap(err) } ok, err := pk.Verify(m.Secret, m.SecretSig) if err != nil { return errcode.ErrCode_ErrCryptoSignatureVerification.Wrap(err) } if !ok { return errcode.ErrCode_ErrCryptoSignatureVerification } return nil } // GroupIDAsString returns the group pub key as a string func (m *Group) GroupIDAsString() string { return hex.EncodeToString(m.PublicKey) } func (m *Group) Copy() *Group { return &Group{ PublicKey: m.PublicKey, Secret: m.Secret, SecretSig: m.SecretSig, GroupType: m.GroupType, SignPub: m.SignPub, } } const CurrentGroupVersion = 1 // NewGroupMultiMember creates a new Group object and an invitation to be used by // the first member of the group func NewGroupMultiMember() (*Group, crypto.PrivKey, error) { priv, pub, err := crypto.GenerateEd25519Key(crand.Reader) if err != nil { return nil, nil, errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err) } pubBytes, err := pub.Raw() if err != nil { return nil, nil, errcode.ErrCode_ErrSerialization.Wrap(err) } signing, _, err := crypto.GenerateEd25519Key(crand.Reader) if err != nil { return nil, nil, errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err) } signingBytes, err := cryptoutil.SeedFromEd25519PrivateKey(signing) if err != nil { return nil, nil, errcode.ErrCode_ErrSerialization.Wrap(err) } skSig, err := priv.Sign(signingBytes) if err != nil { return nil, nil, errcode.ErrCode_ErrCryptoSignature.Wrap(err) } group := &Group{ PublicKey: pubBytes, Secret: signingBytes, SecretSig: skSig, GroupType: GroupType_GroupTypeMultiMember, } updateKey, err := group.GetLinkKeyArray() if err != nil { return nil, nil, errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err) } linkKeySig, err := priv.Sign(updateKey[:]) if err != nil { return nil, nil, errcode.ErrCode_ErrCryptoSignature.Wrap(err) } group.LinkKeySig = linkKeySig return group, priv, nil } func ComputeLinkKey(publicKey, secret []byte) (*[cryptoutil.KeySize]byte, error) { arr := [cryptoutil.KeySize]byte{} kdf := hkdf.New(sha3.New256, secret, nil, publicKey) if _, err := io.ReadFull(kdf, arr[:]); err != nil { return nil, errcode.ErrCode_ErrStreamRead.Wrap(err) } return &arr, nil } func (m *Group) GetLinkKeyArray() (*[cryptoutil.KeySize]byte, error) { if len(m.GetLinkKey()) == cryptoutil.KeySize { arr := [cryptoutil.KeySize]byte{} for i, c := range m.GetLinkKey() { arr[i] = c } return &arr, nil } return ComputeLinkKey(m.GetPublicKey(), m.GetSecret()) } func (m *Group) GetSharedSecret() *[cryptoutil.KeySize]byte { sharedSecret := [cryptoutil.KeySize]byte{} copy(sharedSecret[:], m.GetSecret()) return &sharedSecret } ================================================ FILE: pkg/protocoltypes/protocoltypes.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.34.2 // protoc (unknown) // source: protocoltypes.proto package protocoltypes import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type GroupType int32 const ( // GroupTypeUndefined indicates that the value has not been set. For example, happens if group is replicated. GroupType_GroupTypeUndefined GroupType = 0 // GroupTypeAccount is the group managing an account, available to all its devices. GroupType_GroupTypeAccount GroupType = 1 // GroupTypeContact is the group created between two accounts, available to all their devices. GroupType_GroupTypeContact GroupType = 2 // GroupTypeMultiMember is a group containing an undefined number of members. GroupType_GroupTypeMultiMember GroupType = 3 ) // Enum value maps for GroupType. var ( GroupType_name = map[int32]string{ 0: "GroupTypeUndefined", 1: "GroupTypeAccount", 2: "GroupTypeContact", 3: "GroupTypeMultiMember", } GroupType_value = map[string]int32{ "GroupTypeUndefined": 0, "GroupTypeAccount": 1, "GroupTypeContact": 2, "GroupTypeMultiMember": 3, } ) func (x GroupType) Enum() *GroupType { p := new(GroupType) *p = x return p } func (x GroupType) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (GroupType) Descriptor() protoreflect.EnumDescriptor { return file_protocoltypes_proto_enumTypes[0].Descriptor() } func (GroupType) Type() protoreflect.EnumType { return &file_protocoltypes_proto_enumTypes[0] } func (x GroupType) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use GroupType.Descriptor instead. func (GroupType) EnumDescriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{0} } type EventType int32 const ( // EventTypeUndefined indicates that the value has not been set. Should not happen. EventType_EventTypeUndefined EventType = 0 // EventTypeGroupMemberDeviceAdded indicates the payload includes that a member has added their device to the group EventType_EventTypeGroupMemberDeviceAdded EventType = 1 // EventTypeGroupDeviceChainKeyAdded indicates the payload includes that a member has sent their device chain key to another member EventType_EventTypeGroupDeviceChainKeyAdded EventType = 2 // EventTypeAccountGroupJoined indicates the payload includes that the account has joined a group EventType_EventTypeAccountGroupJoined EventType = 101 // EventTypeAccountGroupLeft indicates the payload includes that the account has left a group EventType_EventTypeAccountGroupLeft EventType = 102 // EventTypeAccountContactRequestDisabled indicates the payload includes that the account has disabled incoming contact requests EventType_EventTypeAccountContactRequestDisabled EventType = 103 // EventTypeAccountContactRequestEnabled indicates the payload includes that the account has enabled incoming contact requests EventType_EventTypeAccountContactRequestEnabled EventType = 104 // EventTypeAccountContactRequestReferenceReset indicates the payload includes that the account has a new contact request rendezvous seed EventType_EventTypeAccountContactRequestReferenceReset EventType = 105 // EventTypeAccountContactRequestOutgoingEnqueued indicates the payload includes that the account will attempt to send a new contact request EventType_EventTypeAccountContactRequestOutgoingEnqueued EventType = 106 // EventTypeAccountContactRequestOutgoingSent indicates the payload includes that the account has sent a contact request EventType_EventTypeAccountContactRequestOutgoingSent EventType = 107 // EventTypeAccountContactRequestIncomingReceived indicates the payload includes that the account has received a contact request EventType_EventTypeAccountContactRequestIncomingReceived EventType = 108 // EventTypeAccountContactRequestIncomingDiscarded indicates the payload includes that the account has ignored a contact request EventType_EventTypeAccountContactRequestIncomingDiscarded EventType = 109 // EventTypeAccountContactRequestIncomingAccepted indicates the payload includes that the account has accepted a contact request EventType_EventTypeAccountContactRequestIncomingAccepted EventType = 110 // EventTypeAccountContactBlocked indicates the payload includes that the account has blocked a contact EventType_EventTypeAccountContactBlocked EventType = 111 // EventTypeAccountContactUnblocked indicates the payload includes that the account has unblocked a contact EventType_EventTypeAccountContactUnblocked EventType = 112 // EventTypeContactAliasKeyAdded indicates the payload includes that the contact group has received an alias key EventType_EventTypeContactAliasKeyAdded EventType = 201 // EventTypeMultiMemberGroupAliasResolverAdded indicates the payload includes that a member of the group sent their alias proof EventType_EventTypeMultiMemberGroupAliasResolverAdded EventType = 301 // EventTypeMultiMemberGroupInitialMemberAnnounced indicates the payload includes that a member has authenticated themselves as the group owner EventType_EventTypeMultiMemberGroupInitialMemberAnnounced EventType = 302 // EventTypeMultiMemberGroupAdminRoleGranted indicates the payload includes that an admin of the group granted another member as an admin EventType_EventTypeMultiMemberGroupAdminRoleGranted EventType = 303 // EventTypeGroupReplicating indicates that the group has been registered for replication on a server EventType_EventTypeGroupReplicating EventType = 403 // EventTypeAccountVerifiedCredentialRegistered EventType_EventTypeAccountVerifiedCredentialRegistered EventType = 500 // EventTypeGroupMetadataPayloadSent indicates the payload includes an app specific event, unlike messages stored on the message store it is encrypted using a static key EventType_EventTypeGroupMetadataPayloadSent EventType = 1001 ) // Enum value maps for EventType. var ( EventType_name = map[int32]string{ 0: "EventTypeUndefined", 1: "EventTypeGroupMemberDeviceAdded", 2: "EventTypeGroupDeviceChainKeyAdded", 101: "EventTypeAccountGroupJoined", 102: "EventTypeAccountGroupLeft", 103: "EventTypeAccountContactRequestDisabled", 104: "EventTypeAccountContactRequestEnabled", 105: "EventTypeAccountContactRequestReferenceReset", 106: "EventTypeAccountContactRequestOutgoingEnqueued", 107: "EventTypeAccountContactRequestOutgoingSent", 108: "EventTypeAccountContactRequestIncomingReceived", 109: "EventTypeAccountContactRequestIncomingDiscarded", 110: "EventTypeAccountContactRequestIncomingAccepted", 111: "EventTypeAccountContactBlocked", 112: "EventTypeAccountContactUnblocked", 201: "EventTypeContactAliasKeyAdded", 301: "EventTypeMultiMemberGroupAliasResolverAdded", 302: "EventTypeMultiMemberGroupInitialMemberAnnounced", 303: "EventTypeMultiMemberGroupAdminRoleGranted", 403: "EventTypeGroupReplicating", 500: "EventTypeAccountVerifiedCredentialRegistered", 1001: "EventTypeGroupMetadataPayloadSent", } EventType_value = map[string]int32{ "EventTypeUndefined": 0, "EventTypeGroupMemberDeviceAdded": 1, "EventTypeGroupDeviceChainKeyAdded": 2, "EventTypeAccountGroupJoined": 101, "EventTypeAccountGroupLeft": 102, "EventTypeAccountContactRequestDisabled": 103, "EventTypeAccountContactRequestEnabled": 104, "EventTypeAccountContactRequestReferenceReset": 105, "EventTypeAccountContactRequestOutgoingEnqueued": 106, "EventTypeAccountContactRequestOutgoingSent": 107, "EventTypeAccountContactRequestIncomingReceived": 108, "EventTypeAccountContactRequestIncomingDiscarded": 109, "EventTypeAccountContactRequestIncomingAccepted": 110, "EventTypeAccountContactBlocked": 111, "EventTypeAccountContactUnblocked": 112, "EventTypeContactAliasKeyAdded": 201, "EventTypeMultiMemberGroupAliasResolverAdded": 301, "EventTypeMultiMemberGroupInitialMemberAnnounced": 302, "EventTypeMultiMemberGroupAdminRoleGranted": 303, "EventTypeGroupReplicating": 403, "EventTypeAccountVerifiedCredentialRegistered": 500, "EventTypeGroupMetadataPayloadSent": 1001, } ) func (x EventType) Enum() *EventType { p := new(EventType) *p = x return p } func (x EventType) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (EventType) Descriptor() protoreflect.EnumDescriptor { return file_protocoltypes_proto_enumTypes[1].Descriptor() } func (EventType) Type() protoreflect.EnumType { return &file_protocoltypes_proto_enumTypes[1] } func (x EventType) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use EventType.Descriptor instead. func (EventType) EnumDescriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{1} } type DebugInspectGroupLogType int32 const ( DebugInspectGroupLogType_DebugInspectGroupLogTypeUndefined DebugInspectGroupLogType = 0 DebugInspectGroupLogType_DebugInspectGroupLogTypeMessage DebugInspectGroupLogType = 1 DebugInspectGroupLogType_DebugInspectGroupLogTypeMetadata DebugInspectGroupLogType = 2 ) // Enum value maps for DebugInspectGroupLogType. var ( DebugInspectGroupLogType_name = map[int32]string{ 0: "DebugInspectGroupLogTypeUndefined", 1: "DebugInspectGroupLogTypeMessage", 2: "DebugInspectGroupLogTypeMetadata", } DebugInspectGroupLogType_value = map[string]int32{ "DebugInspectGroupLogTypeUndefined": 0, "DebugInspectGroupLogTypeMessage": 1, "DebugInspectGroupLogTypeMetadata": 2, } ) func (x DebugInspectGroupLogType) Enum() *DebugInspectGroupLogType { p := new(DebugInspectGroupLogType) *p = x return p } func (x DebugInspectGroupLogType) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (DebugInspectGroupLogType) Descriptor() protoreflect.EnumDescriptor { return file_protocoltypes_proto_enumTypes[2].Descriptor() } func (DebugInspectGroupLogType) Type() protoreflect.EnumType { return &file_protocoltypes_proto_enumTypes[2] } func (x DebugInspectGroupLogType) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use DebugInspectGroupLogType.Descriptor instead. func (DebugInspectGroupLogType) EnumDescriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{2} } type ContactState int32 const ( ContactState_ContactStateUndefined ContactState = 0 ContactState_ContactStateToRequest ContactState = 1 ContactState_ContactStateReceived ContactState = 2 ContactState_ContactStateAdded ContactState = 3 ContactState_ContactStateRemoved ContactState = 4 ContactState_ContactStateDiscarded ContactState = 5 ContactState_ContactStateBlocked ContactState = 6 ) // Enum value maps for ContactState. var ( ContactState_name = map[int32]string{ 0: "ContactStateUndefined", 1: "ContactStateToRequest", 2: "ContactStateReceived", 3: "ContactStateAdded", 4: "ContactStateRemoved", 5: "ContactStateDiscarded", 6: "ContactStateBlocked", } ContactState_value = map[string]int32{ "ContactStateUndefined": 0, "ContactStateToRequest": 1, "ContactStateReceived": 2, "ContactStateAdded": 3, "ContactStateRemoved": 4, "ContactStateDiscarded": 5, "ContactStateBlocked": 6, } ) func (x ContactState) Enum() *ContactState { p := new(ContactState) *p = x return p } func (x ContactState) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (ContactState) Descriptor() protoreflect.EnumDescriptor { return file_protocoltypes_proto_enumTypes[3].Descriptor() } func (ContactState) Type() protoreflect.EnumType { return &file_protocoltypes_proto_enumTypes[3] } func (x ContactState) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use ContactState.Descriptor instead. func (ContactState) EnumDescriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{3} } type Direction int32 const ( Direction_UnknownDir Direction = 0 Direction_InboundDir Direction = 1 Direction_OutboundDir Direction = 2 Direction_BiDir Direction = 3 ) // Enum value maps for Direction. var ( Direction_name = map[int32]string{ 0: "UnknownDir", 1: "InboundDir", 2: "OutboundDir", 3: "BiDir", } Direction_value = map[string]int32{ "UnknownDir": 0, "InboundDir": 1, "OutboundDir": 2, "BiDir": 3, } ) func (x Direction) Enum() *Direction { p := new(Direction) *p = x return p } func (x Direction) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (Direction) Descriptor() protoreflect.EnumDescriptor { return file_protocoltypes_proto_enumTypes[4].Descriptor() } func (Direction) Type() protoreflect.EnumType { return &file_protocoltypes_proto_enumTypes[4] } func (x Direction) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use Direction.Descriptor instead. func (Direction) EnumDescriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{4} } type ServiceGetConfiguration_SettingState int32 const ( ServiceGetConfiguration_Unknown ServiceGetConfiguration_SettingState = 0 ServiceGetConfiguration_Enabled ServiceGetConfiguration_SettingState = 1 ServiceGetConfiguration_Disabled ServiceGetConfiguration_SettingState = 2 ServiceGetConfiguration_Unavailable ServiceGetConfiguration_SettingState = 3 ) // Enum value maps for ServiceGetConfiguration_SettingState. var ( ServiceGetConfiguration_SettingState_name = map[int32]string{ 0: "Unknown", 1: "Enabled", 2: "Disabled", 3: "Unavailable", } ServiceGetConfiguration_SettingState_value = map[string]int32{ "Unknown": 0, "Enabled": 1, "Disabled": 2, "Unavailable": 3, } ) func (x ServiceGetConfiguration_SettingState) Enum() *ServiceGetConfiguration_SettingState { p := new(ServiceGetConfiguration_SettingState) *p = x return p } func (x ServiceGetConfiguration_SettingState) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (ServiceGetConfiguration_SettingState) Descriptor() protoreflect.EnumDescriptor { return file_protocoltypes_proto_enumTypes[5].Descriptor() } func (ServiceGetConfiguration_SettingState) Type() protoreflect.EnumType { return &file_protocoltypes_proto_enumTypes[5] } func (x ServiceGetConfiguration_SettingState) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use ServiceGetConfiguration_SettingState.Descriptor instead. func (ServiceGetConfiguration_SettingState) EnumDescriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{34, 0} } type GroupDeviceStatus_Type int32 const ( GroupDeviceStatus_TypeUnknown GroupDeviceStatus_Type = 0 GroupDeviceStatus_TypePeerDisconnected GroupDeviceStatus_Type = 1 GroupDeviceStatus_TypePeerConnected GroupDeviceStatus_Type = 2 GroupDeviceStatus_TypePeerReconnecting GroupDeviceStatus_Type = 3 ) // Enum value maps for GroupDeviceStatus_Type. var ( GroupDeviceStatus_Type_name = map[int32]string{ 0: "TypeUnknown", 1: "TypePeerDisconnected", 2: "TypePeerConnected", 3: "TypePeerReconnecting", } GroupDeviceStatus_Type_value = map[string]int32{ "TypeUnknown": 0, "TypePeerDisconnected": 1, "TypePeerConnected": 2, "TypePeerReconnecting": 3, } ) func (x GroupDeviceStatus_Type) Enum() *GroupDeviceStatus_Type { p := new(GroupDeviceStatus_Type) *p = x return p } func (x GroupDeviceStatus_Type) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (GroupDeviceStatus_Type) Descriptor() protoreflect.EnumDescriptor { return file_protocoltypes_proto_enumTypes[6].Descriptor() } func (GroupDeviceStatus_Type) Type() protoreflect.EnumType { return &file_protocoltypes_proto_enumTypes[6] } func (x GroupDeviceStatus_Type) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use GroupDeviceStatus_Type.Descriptor instead. func (GroupDeviceStatus_Type) EnumDescriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{62, 0} } type GroupDeviceStatus_Transport int32 const ( GroupDeviceStatus_TptUnknown GroupDeviceStatus_Transport = 0 GroupDeviceStatus_TptLAN GroupDeviceStatus_Transport = 1 GroupDeviceStatus_TptWAN GroupDeviceStatus_Transport = 2 GroupDeviceStatus_TptProximity GroupDeviceStatus_Transport = 3 ) // Enum value maps for GroupDeviceStatus_Transport. var ( GroupDeviceStatus_Transport_name = map[int32]string{ 0: "TptUnknown", 1: "TptLAN", 2: "TptWAN", 3: "TptProximity", } GroupDeviceStatus_Transport_value = map[string]int32{ "TptUnknown": 0, "TptLAN": 1, "TptWAN": 2, "TptProximity": 3, } ) func (x GroupDeviceStatus_Transport) Enum() *GroupDeviceStatus_Transport { p := new(GroupDeviceStatus_Transport) *p = x return p } func (x GroupDeviceStatus_Transport) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (GroupDeviceStatus_Transport) Descriptor() protoreflect.EnumDescriptor { return file_protocoltypes_proto_enumTypes[7].Descriptor() } func (GroupDeviceStatus_Transport) Type() protoreflect.EnumType { return &file_protocoltypes_proto_enumTypes[7] } func (x GroupDeviceStatus_Transport) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use GroupDeviceStatus_Transport.Descriptor instead. func (GroupDeviceStatus_Transport) EnumDescriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{62, 1} } type PeerList_Feature int32 const ( PeerList_UnknownFeature PeerList_Feature = 0 PeerList_WeshFeature PeerList_Feature = 1 PeerList_BLEFeature PeerList_Feature = 2 PeerList_LocalFeature PeerList_Feature = 3 PeerList_TorFeature PeerList_Feature = 4 PeerList_QuicFeature PeerList_Feature = 5 ) // Enum value maps for PeerList_Feature. var ( PeerList_Feature_name = map[int32]string{ 0: "UnknownFeature", 1: "WeshFeature", 2: "BLEFeature", 3: "LocalFeature", 4: "TorFeature", 5: "QuicFeature", } PeerList_Feature_value = map[string]int32{ "UnknownFeature": 0, "WeshFeature": 1, "BLEFeature": 2, "LocalFeature": 3, "TorFeature": 4, "QuicFeature": 5, } ) func (x PeerList_Feature) Enum() *PeerList_Feature { p := new(PeerList_Feature) *p = x return p } func (x PeerList_Feature) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (PeerList_Feature) Descriptor() protoreflect.EnumDescriptor { return file_protocoltypes_proto_enumTypes[8].Descriptor() } func (PeerList_Feature) Type() protoreflect.EnumType { return &file_protocoltypes_proto_enumTypes[8] } func (x PeerList_Feature) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use PeerList_Feature.Descriptor instead. func (PeerList_Feature) EnumDescriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{75, 0} } // Account describes all the secrets that identifies an Account type Account struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // group specifies which group is used to manage the account Group *Group `protobuf:"bytes,1,opt,name=group,proto3" json:"group,omitempty"` // 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 AccountPrivateKey []byte `protobuf:"bytes,2,opt,name=account_private_key,json=accountPrivateKey,proto3" json:"account_private_key,omitempty"` // 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 AliasPrivateKey []byte `protobuf:"bytes,3,opt,name=alias_private_key,json=aliasPrivateKey,proto3" json:"alias_private_key,omitempty"` // public_rendezvous_seed, rendezvous seed used for direct communication PublicRendezvousSeed []byte `protobuf:"bytes,4,opt,name=public_rendezvous_seed,json=publicRendezvousSeed,proto3" json:"public_rendezvous_seed,omitempty"` } func (x *Account) Reset() { *x = Account{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Account) String() string { return protoimpl.X.MessageStringOf(x) } func (*Account) ProtoMessage() {} func (x *Account) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Account.ProtoReflect.Descriptor instead. func (*Account) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{0} } func (x *Account) GetGroup() *Group { if x != nil { return x.Group } return nil } func (x *Account) GetAccountPrivateKey() []byte { if x != nil { return x.AccountPrivateKey } return nil } func (x *Account) GetAliasPrivateKey() []byte { if x != nil { return x.AliasPrivateKey } return nil } func (x *Account) GetPublicRendezvousSeed() []byte { if x != nil { return x.PublicRendezvousSeed } return nil } // Group define a group and is enough to invite someone to it type Group struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // public_key is the identifier of the group, it signs the group secret and the initial member of a multi-member group PublicKey []byte `protobuf:"bytes,1,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` // secret is the symmetric secret of the group, which is used to encrypt the metadata Secret []byte `protobuf:"bytes,2,opt,name=secret,proto3" json:"secret,omitempty"` // secret_sig is the signature of the secret used to ensure the validity of the group SecretSig []byte `protobuf:"bytes,3,opt,name=secret_sig,json=secretSig,proto3" json:"secret_sig,omitempty"` // group_type specifies the type of the group, used to determine how device chain key is generated GroupType GroupType `protobuf:"varint,4,opt,name=group_type,json=groupType,proto3,enum=weshnet.protocol.v1.GroupType" json:"group_type,omitempty"` // sign_pub is the signature public key used to verify entries, not required when secret and secret_sig are provided SignPub []byte `protobuf:"bytes,5,opt,name=sign_pub,json=signPub,proto3" json:"sign_pub,omitempty"` // link_key is the secret key used to exchange group updates and links to attachments, useful for replication services LinkKey []byte `protobuf:"bytes,6,opt,name=link_key,json=linkKey,proto3" json:"link_key,omitempty"` // link_key_sig is the signature of the link_key using the group private key LinkKeySig []byte `protobuf:"bytes,7,opt,name=link_key_sig,json=linkKeySig,proto3" json:"link_key_sig,omitempty"` } func (x *Group) Reset() { *x = Group{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Group) String() string { return protoimpl.X.MessageStringOf(x) } func (*Group) ProtoMessage() {} func (x *Group) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Group.ProtoReflect.Descriptor instead. func (*Group) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{1} } func (x *Group) GetPublicKey() []byte { if x != nil { return x.PublicKey } return nil } func (x *Group) GetSecret() []byte { if x != nil { return x.Secret } return nil } func (x *Group) GetSecretSig() []byte { if x != nil { return x.SecretSig } return nil } func (x *Group) GetGroupType() GroupType { if x != nil { return x.GroupType } return GroupType_GroupTypeUndefined } func (x *Group) GetSignPub() []byte { if x != nil { return x.SignPub } return nil } func (x *Group) GetLinkKey() []byte { if x != nil { return x.LinkKey } return nil } func (x *Group) GetLinkKeySig() []byte { if x != nil { return x.LinkKeySig } return nil } type GroupHeadsExport struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // public_key is the identifier of the group, it signs the group secret and the initial member of a multi-member group PublicKey []byte `protobuf:"bytes,1,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` // sign_pub is the signature public key used to verify entries SignPub []byte `protobuf:"bytes,2,opt,name=sign_pub,json=signPub,proto3" json:"sign_pub,omitempty"` // metadata_heads_cids are the heads of the metadata store that should be restored from an export MetadataHeadsCids [][]byte `protobuf:"bytes,3,rep,name=metadata_heads_cids,json=metadataHeadsCids,proto3" json:"metadata_heads_cids,omitempty"` // messages_heads_cids are the heads of the metadata store that should be restored from an export MessagesHeadsCids [][]byte `protobuf:"bytes,4,rep,name=messages_heads_cids,json=messagesHeadsCids,proto3" json:"messages_heads_cids,omitempty"` // link_key LinkKey []byte `protobuf:"bytes,5,opt,name=link_key,json=linkKey,proto3" json:"link_key,omitempty"` } func (x *GroupHeadsExport) Reset() { *x = GroupHeadsExport{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *GroupHeadsExport) String() string { return protoimpl.X.MessageStringOf(x) } func (*GroupHeadsExport) ProtoMessage() {} func (x *GroupHeadsExport) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GroupHeadsExport.ProtoReflect.Descriptor instead. func (*GroupHeadsExport) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{2} } func (x *GroupHeadsExport) GetPublicKey() []byte { if x != nil { return x.PublicKey } return nil } func (x *GroupHeadsExport) GetSignPub() []byte { if x != nil { return x.SignPub } return nil } func (x *GroupHeadsExport) GetMetadataHeadsCids() [][]byte { if x != nil { return x.MetadataHeadsCids } return nil } func (x *GroupHeadsExport) GetMessagesHeadsCids() [][]byte { if x != nil { return x.MessagesHeadsCids } return nil } func (x *GroupHeadsExport) GetLinkKey() []byte { if x != nil { return x.LinkKey } return nil } // GroupMetadata is used in GroupEnvelope and only readable by invited group members type GroupMetadata struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // event_type defines which event type is used EventType EventType `protobuf:"varint,1,opt,name=event_type,json=eventType,proto3,enum=weshnet.protocol.v1.EventType" json:"event_type,omitempty"` // the serialization depends on event_type, event is symmetrically encrypted Payload []byte `protobuf:"bytes,2,opt,name=payload,proto3" json:"payload,omitempty"` // sig is the signature of the payload, it depends on the event_type for the used key Sig []byte `protobuf:"bytes,3,opt,name=sig,proto3" json:"sig,omitempty"` // protocol_metadata is protocol layer data ProtocolMetadata *ProtocolMetadata `protobuf:"bytes,4,opt,name=protocol_metadata,json=protocolMetadata,proto3" json:"protocol_metadata,omitempty"` } func (x *GroupMetadata) Reset() { *x = GroupMetadata{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *GroupMetadata) String() string { return protoimpl.X.MessageStringOf(x) } func (*GroupMetadata) ProtoMessage() {} func (x *GroupMetadata) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GroupMetadata.ProtoReflect.Descriptor instead. func (*GroupMetadata) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{3} } func (x *GroupMetadata) GetEventType() EventType { if x != nil { return x.EventType } return EventType_EventTypeUndefined } func (x *GroupMetadata) GetPayload() []byte { if x != nil { return x.Payload } return nil } func (x *GroupMetadata) GetSig() []byte { if x != nil { return x.Sig } return nil } func (x *GroupMetadata) GetProtocolMetadata() *ProtocolMetadata { if x != nil { return x.ProtocolMetadata } return nil } // GroupEnvelope is a publicly exposed structure containing a group metadata event type GroupEnvelope struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // nonce is used to encrypt the message Nonce []byte `protobuf:"bytes,1,opt,name=nonce,proto3" json:"nonce,omitempty"` // event is encrypted using a symmetric key shared among group members Event []byte `protobuf:"bytes,2,opt,name=event,proto3" json:"event,omitempty"` } func (x *GroupEnvelope) Reset() { *x = GroupEnvelope{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *GroupEnvelope) String() string { return protoimpl.X.MessageStringOf(x) } func (*GroupEnvelope) ProtoMessage() {} func (x *GroupEnvelope) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GroupEnvelope.ProtoReflect.Descriptor instead. func (*GroupEnvelope) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{4} } func (x *GroupEnvelope) GetNonce() []byte { if x != nil { return x.Nonce } return nil } func (x *GroupEnvelope) GetEvent() []byte { if x != nil { return x.Event } return nil } // MessageHeaders is used in MessageEnvelope and only readable by invited group members type MessageHeaders struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // counter is the current counter value for the specified device Counter uint64 `protobuf:"varint,1,opt,name=counter,proto3" json:"counter,omitempty"` // device_pk is the public key of the device sending the message DevicePk []byte `protobuf:"bytes,2,opt,name=device_pk,json=devicePk,proto3" json:"device_pk,omitempty"` // sig is the signature of the encrypted message using the device's private key Sig []byte `protobuf:"bytes,3,opt,name=sig,proto3" json:"sig,omitempty"` // metadata allow to pass custom informations Metadata 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"` } func (x *MessageHeaders) Reset() { *x = MessageHeaders{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *MessageHeaders) String() string { return protoimpl.X.MessageStringOf(x) } func (*MessageHeaders) ProtoMessage() {} func (x *MessageHeaders) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MessageHeaders.ProtoReflect.Descriptor instead. func (*MessageHeaders) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{5} } func (x *MessageHeaders) GetCounter() uint64 { if x != nil { return x.Counter } return 0 } func (x *MessageHeaders) GetDevicePk() []byte { if x != nil { return x.DevicePk } return nil } func (x *MessageHeaders) GetSig() []byte { if x != nil { return x.Sig } return nil } func (x *MessageHeaders) GetMetadata() map[string]string { if x != nil { return x.Metadata } return nil } type ProtocolMetadata struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *ProtocolMetadata) Reset() { *x = ProtocolMetadata{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ProtocolMetadata) String() string { return protoimpl.X.MessageStringOf(x) } func (*ProtocolMetadata) ProtoMessage() {} func (x *ProtocolMetadata) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ProtocolMetadata.ProtoReflect.Descriptor instead. func (*ProtocolMetadata) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{6} } // EncryptedMessage is used in MessageEnvelope and only readable by groups members that joined before the message was sent type EncryptedMessage struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // plaintext is the app layer data Plaintext []byte `protobuf:"bytes,1,opt,name=plaintext,proto3" json:"plaintext,omitempty"` // protocol_metadata is protocol layer data ProtocolMetadata *ProtocolMetadata `protobuf:"bytes,2,opt,name=protocol_metadata,json=protocolMetadata,proto3" json:"protocol_metadata,omitempty"` } func (x *EncryptedMessage) Reset() { *x = EncryptedMessage{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *EncryptedMessage) String() string { return protoimpl.X.MessageStringOf(x) } func (*EncryptedMessage) ProtoMessage() {} func (x *EncryptedMessage) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EncryptedMessage.ProtoReflect.Descriptor instead. func (*EncryptedMessage) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{7} } func (x *EncryptedMessage) GetPlaintext() []byte { if x != nil { return x.Plaintext } return nil } func (x *EncryptedMessage) GetProtocolMetadata() *ProtocolMetadata { if x != nil { return x.ProtocolMetadata } return nil } // MessageEnvelope is a publicly exposed structure containing a group secure message type MessageEnvelope struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // message_headers is an encrypted serialization using a symmetric key of a MessageHeaders message MessageHeaders []byte `protobuf:"bytes,1,opt,name=message_headers,json=messageHeaders,proto3" json:"message_headers,omitempty"` // message is an encrypted message, only readable by group members who previously received the appropriate chain key Message []byte `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` // nonce is a nonce for message headers Nonce []byte `protobuf:"bytes,3,opt,name=nonce,proto3" json:"nonce,omitempty"` } func (x *MessageEnvelope) Reset() { *x = MessageEnvelope{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *MessageEnvelope) String() string { return protoimpl.X.MessageStringOf(x) } func (*MessageEnvelope) ProtoMessage() {} func (x *MessageEnvelope) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MessageEnvelope.ProtoReflect.Descriptor instead. func (*MessageEnvelope) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{8} } func (x *MessageEnvelope) GetMessageHeaders() []byte { if x != nil { return x.MessageHeaders } return nil } func (x *MessageEnvelope) GetMessage() []byte { if x != nil { return x.Message } return nil } func (x *MessageEnvelope) GetNonce() []byte { if x != nil { return x.Nonce } return nil } // EventContext adds context (its id, its parents and its attachments) to an event type EventContext struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // id is the CID of the underlying OrbitDB event Id []byte `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // id are the the CIDs of the underlying parents of the OrbitDB event ParentIds [][]byte `protobuf:"bytes,2,rep,name=parent_ids,json=parentIds,proto3" json:"parent_ids,omitempty"` // group_pk receiving the event GroupPk []byte `protobuf:"bytes,3,opt,name=group_pk,json=groupPk,proto3" json:"group_pk,omitempty"` } func (x *EventContext) Reset() { *x = EventContext{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *EventContext) String() string { return protoimpl.X.MessageStringOf(x) } func (*EventContext) ProtoMessage() {} func (x *EventContext) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EventContext.ProtoReflect.Descriptor instead. func (*EventContext) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{9} } func (x *EventContext) GetId() []byte { if x != nil { return x.Id } return nil } func (x *EventContext) GetParentIds() [][]byte { if x != nil { return x.ParentIds } return nil } func (x *EventContext) GetGroupPk() []byte { if x != nil { return x.GroupPk } return nil } // GroupMetadataPayloadSent is an app defined message, accessible to future group members type GroupMetadataPayloadSent struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // device_pk is the device sending the event, signs the message DevicePk []byte `protobuf:"bytes,1,opt,name=device_pk,json=devicePk,proto3" json:"device_pk,omitempty"` // message is the payload Message []byte `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` } func (x *GroupMetadataPayloadSent) Reset() { *x = GroupMetadataPayloadSent{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *GroupMetadataPayloadSent) String() string { return protoimpl.X.MessageStringOf(x) } func (*GroupMetadataPayloadSent) ProtoMessage() {} func (x *GroupMetadataPayloadSent) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GroupMetadataPayloadSent.ProtoReflect.Descriptor instead. func (*GroupMetadataPayloadSent) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{10} } func (x *GroupMetadataPayloadSent) GetDevicePk() []byte { if x != nil { return x.DevicePk } return nil } func (x *GroupMetadataPayloadSent) GetMessage() []byte { if x != nil { return x.Message } return nil } // ContactAliasKeyAdded is an event type where ones shares their alias public key type ContactAliasKeyAdded struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // device_pk is the device sending the event, signs the message DevicePk []byte `protobuf:"bytes,1,opt,name=device_pk,json=devicePk,proto3" json:"device_pk,omitempty"` // alias_pk is the alias key which will be used to verify a contact identity AliasPk []byte `protobuf:"bytes,2,opt,name=alias_pk,json=aliasPk,proto3" json:"alias_pk,omitempty"` } func (x *ContactAliasKeyAdded) Reset() { *x = ContactAliasKeyAdded{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ContactAliasKeyAdded) String() string { return protoimpl.X.MessageStringOf(x) } func (*ContactAliasKeyAdded) ProtoMessage() {} func (x *ContactAliasKeyAdded) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ContactAliasKeyAdded.ProtoReflect.Descriptor instead. func (*ContactAliasKeyAdded) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{11} } func (x *ContactAliasKeyAdded) GetDevicePk() []byte { if x != nil { return x.DevicePk } return nil } func (x *ContactAliasKeyAdded) GetAliasPk() []byte { if x != nil { return x.AliasPk } return nil } // GroupMemberDeviceAdded is an event which indicates to a group a new device (and eventually a new member) is joining it // When added on AccountGroup, this event should be followed by appropriate GroupMemberDeviceAdded and GroupDeviceChainKeyAdded events type GroupMemberDeviceAdded struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // member_pk is the member sending the event MemberPk []byte `protobuf:"bytes,1,opt,name=member_pk,json=memberPk,proto3" json:"member_pk,omitempty"` // device_pk is the device sending the event, signs the message DevicePk []byte `protobuf:"bytes,2,opt,name=device_pk,json=devicePk,proto3" json:"device_pk,omitempty"` // member_sig is used to prove the ownership of the member pk MemberSig []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 } func (x *GroupMemberDeviceAdded) Reset() { *x = GroupMemberDeviceAdded{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *GroupMemberDeviceAdded) String() string { return protoimpl.X.MessageStringOf(x) } func (*GroupMemberDeviceAdded) ProtoMessage() {} func (x *GroupMemberDeviceAdded) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GroupMemberDeviceAdded.ProtoReflect.Descriptor instead. func (*GroupMemberDeviceAdded) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{12} } func (x *GroupMemberDeviceAdded) GetMemberPk() []byte { if x != nil { return x.MemberPk } return nil } func (x *GroupMemberDeviceAdded) GetDevicePk() []byte { if x != nil { return x.DevicePk } return nil } func (x *GroupMemberDeviceAdded) GetMemberSig() []byte { if x != nil { return x.MemberSig } return nil } // DeviceChainKey is a chain key, which will be encrypted for a specific member of the group type DeviceChainKey struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // chain_key is the current value of the chain key of the group device ChainKey []byte `protobuf:"bytes,1,opt,name=chain_key,json=chainKey,proto3" json:"chain_key,omitempty"` // counter is the current value of the counter of the group device Counter uint64 `protobuf:"varint,2,opt,name=counter,proto3" json:"counter,omitempty"` } func (x *DeviceChainKey) Reset() { *x = DeviceChainKey{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *DeviceChainKey) String() string { return protoimpl.X.MessageStringOf(x) } func (*DeviceChainKey) ProtoMessage() {} func (x *DeviceChainKey) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DeviceChainKey.ProtoReflect.Descriptor instead. func (*DeviceChainKey) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{13} } func (x *DeviceChainKey) GetChainKey() []byte { if x != nil { return x.ChainKey } return nil } func (x *DeviceChainKey) GetCounter() uint64 { if x != nil { return x.Counter } return 0 } // GroupDeviceChainKeyAdded is an event which indicates to a group member a device chain key type GroupDeviceChainKeyAdded struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // device_pk is the device sending the event, signs the message DevicePk []byte `protobuf:"bytes,1,opt,name=device_pk,json=devicePk,proto3" json:"device_pk,omitempty"` // dest_member_pk is the member who should receive the secret DestMemberPk []byte `protobuf:"bytes,2,opt,name=dest_member_pk,json=destMemberPk,proto3" json:"dest_member_pk,omitempty"` // payload is the serialization of Payload encrypted for the specified member Payload []byte `protobuf:"bytes,3,opt,name=payload,proto3" json:"payload,omitempty"` } func (x *GroupDeviceChainKeyAdded) Reset() { *x = GroupDeviceChainKeyAdded{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *GroupDeviceChainKeyAdded) String() string { return protoimpl.X.MessageStringOf(x) } func (*GroupDeviceChainKeyAdded) ProtoMessage() {} func (x *GroupDeviceChainKeyAdded) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GroupDeviceChainKeyAdded.ProtoReflect.Descriptor instead. func (*GroupDeviceChainKeyAdded) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{14} } func (x *GroupDeviceChainKeyAdded) GetDevicePk() []byte { if x != nil { return x.DevicePk } return nil } func (x *GroupDeviceChainKeyAdded) GetDestMemberPk() []byte { if x != nil { return x.DestMemberPk } return nil } func (x *GroupDeviceChainKeyAdded) GetPayload() []byte { if x != nil { return x.Payload } return nil } // MultiMemberGroupAliasResolverAdded indicates that a group member want to disclose their presence in the group to their contacts type MultiMemberGroupAliasResolverAdded struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // device_pk is the device sending the event, signs the message DevicePk []byte `protobuf:"bytes,1,opt,name=device_pk,json=devicePk,proto3" json:"device_pk,omitempty"` // 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) AliasResolver []byte `protobuf:"bytes,2,opt,name=alias_resolver,json=aliasResolver,proto3" json:"alias_resolver,omitempty"` // alias_proof ensures that the associated alias_resolver has been issued by the right account // Generated using aliasSKSig(GroupID) AliasProof []byte `protobuf:"bytes,3,opt,name=alias_proof,json=aliasProof,proto3" json:"alias_proof,omitempty"` } func (x *MultiMemberGroupAliasResolverAdded) Reset() { *x = MultiMemberGroupAliasResolverAdded{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *MultiMemberGroupAliasResolverAdded) String() string { return protoimpl.X.MessageStringOf(x) } func (*MultiMemberGroupAliasResolverAdded) ProtoMessage() {} func (x *MultiMemberGroupAliasResolverAdded) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MultiMemberGroupAliasResolverAdded.ProtoReflect.Descriptor instead. func (*MultiMemberGroupAliasResolverAdded) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{15} } func (x *MultiMemberGroupAliasResolverAdded) GetDevicePk() []byte { if x != nil { return x.DevicePk } return nil } func (x *MultiMemberGroupAliasResolverAdded) GetAliasResolver() []byte { if x != nil { return x.AliasResolver } return nil } func (x *MultiMemberGroupAliasResolverAdded) GetAliasProof() []byte { if x != nil { return x.AliasProof } return nil } // MultiMemberGroupAdminRoleGranted indicates that a group admin allows another group member to act as an admin type MultiMemberGroupAdminRoleGranted struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // device_pk is the device sending the event, signs the message, must be the device of an admin of the group DevicePk []byte `protobuf:"bytes,1,opt,name=device_pk,json=devicePk,proto3" json:"device_pk,omitempty"` // grantee_member_pk is the member public key of the member granted of the admin role GranteeMemberPk []byte `protobuf:"bytes,2,opt,name=grantee_member_pk,json=granteeMemberPk,proto3" json:"grantee_member_pk,omitempty"` } func (x *MultiMemberGroupAdminRoleGranted) Reset() { *x = MultiMemberGroupAdminRoleGranted{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *MultiMemberGroupAdminRoleGranted) String() string { return protoimpl.X.MessageStringOf(x) } func (*MultiMemberGroupAdminRoleGranted) ProtoMessage() {} func (x *MultiMemberGroupAdminRoleGranted) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MultiMemberGroupAdminRoleGranted.ProtoReflect.Descriptor instead. func (*MultiMemberGroupAdminRoleGranted) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{16} } func (x *MultiMemberGroupAdminRoleGranted) GetDevicePk() []byte { if x != nil { return x.DevicePk } return nil } func (x *MultiMemberGroupAdminRoleGranted) GetGranteeMemberPk() []byte { if x != nil { return x.GranteeMemberPk } return nil } // MultiMemberGroupInitialMemberAnnounced indicates that a member is the group creator, this event is signed using the group ID private key type MultiMemberGroupInitialMemberAnnounced struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // member_pk is the public key of the member who is the group creator MemberPk []byte `protobuf:"bytes,1,opt,name=member_pk,json=memberPk,proto3" json:"member_pk,omitempty"` } func (x *MultiMemberGroupInitialMemberAnnounced) Reset() { *x = MultiMemberGroupInitialMemberAnnounced{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *MultiMemberGroupInitialMemberAnnounced) String() string { return protoimpl.X.MessageStringOf(x) } func (*MultiMemberGroupInitialMemberAnnounced) ProtoMessage() {} func (x *MultiMemberGroupInitialMemberAnnounced) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MultiMemberGroupInitialMemberAnnounced.ProtoReflect.Descriptor instead. func (*MultiMemberGroupInitialMemberAnnounced) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{17} } func (x *MultiMemberGroupInitialMemberAnnounced) GetMemberPk() []byte { if x != nil { return x.MemberPk } return nil } // GroupAddAdditionalRendezvousSeed indicates that an additional rendezvous point should be used for data synchronization type GroupAddAdditionalRendezvousSeed struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // device_pk is the device sending the event, signs the message, must be the device of an admin of the group DevicePk []byte `protobuf:"bytes,1,opt,name=device_pk,json=devicePk,proto3" json:"device_pk,omitempty"` // seed is the additional rendezvous point seed which should be used Seed []byte `protobuf:"bytes,2,opt,name=seed,proto3" json:"seed,omitempty"` } func (x *GroupAddAdditionalRendezvousSeed) Reset() { *x = GroupAddAdditionalRendezvousSeed{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *GroupAddAdditionalRendezvousSeed) String() string { return protoimpl.X.MessageStringOf(x) } func (*GroupAddAdditionalRendezvousSeed) ProtoMessage() {} func (x *GroupAddAdditionalRendezvousSeed) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GroupAddAdditionalRendezvousSeed.ProtoReflect.Descriptor instead. func (*GroupAddAdditionalRendezvousSeed) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{18} } func (x *GroupAddAdditionalRendezvousSeed) GetDevicePk() []byte { if x != nil { return x.DevicePk } return nil } func (x *GroupAddAdditionalRendezvousSeed) GetSeed() []byte { if x != nil { return x.Seed } return nil } // GroupRemoveAdditionalRendezvousSeed indicates that a previously added rendezvous point should be removed type GroupRemoveAdditionalRendezvousSeed struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // device_pk is the device sending the event, signs the message, must be the device of an admin of the group DevicePk []byte `protobuf:"bytes,1,opt,name=device_pk,json=devicePk,proto3" json:"device_pk,omitempty"` // seed is the additional rendezvous point seed which should be removed Seed []byte `protobuf:"bytes,2,opt,name=seed,proto3" json:"seed,omitempty"` } func (x *GroupRemoveAdditionalRendezvousSeed) Reset() { *x = GroupRemoveAdditionalRendezvousSeed{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *GroupRemoveAdditionalRendezvousSeed) String() string { return protoimpl.X.MessageStringOf(x) } func (*GroupRemoveAdditionalRendezvousSeed) ProtoMessage() {} func (x *GroupRemoveAdditionalRendezvousSeed) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GroupRemoveAdditionalRendezvousSeed.ProtoReflect.Descriptor instead. func (*GroupRemoveAdditionalRendezvousSeed) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{19} } func (x *GroupRemoveAdditionalRendezvousSeed) GetDevicePk() []byte { if x != nil { return x.DevicePk } return nil } func (x *GroupRemoveAdditionalRendezvousSeed) GetSeed() []byte { if x != nil { return x.Seed } return nil } // AccountGroupJoined indicates that the account is now part of a new group type AccountGroupJoined struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // device_pk is the device sending the event, signs the message DevicePk []byte `protobuf:"bytes,1,opt,name=device_pk,json=devicePk,proto3" json:"device_pk,omitempty"` // group describe the joined group Group *Group `protobuf:"bytes,2,opt,name=group,proto3" json:"group,omitempty"` } func (x *AccountGroupJoined) Reset() { *x = AccountGroupJoined{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *AccountGroupJoined) String() string { return protoimpl.X.MessageStringOf(x) } func (*AccountGroupJoined) ProtoMessage() {} func (x *AccountGroupJoined) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AccountGroupJoined.ProtoReflect.Descriptor instead. func (*AccountGroupJoined) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{20} } func (x *AccountGroupJoined) GetDevicePk() []byte { if x != nil { return x.DevicePk } return nil } func (x *AccountGroupJoined) GetGroup() *Group { if x != nil { return x.Group } return nil } // AccountGroupLeft indicates that the account has left a group type AccountGroupLeft struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // device_pk is the device sending the event, signs the message DevicePk []byte `protobuf:"bytes,1,opt,name=device_pk,json=devicePk,proto3" json:"device_pk,omitempty"` // group_pk references the group left GroupPk []byte `protobuf:"bytes,2,opt,name=group_pk,json=groupPk,proto3" json:"group_pk,omitempty"` } func (x *AccountGroupLeft) Reset() { *x = AccountGroupLeft{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *AccountGroupLeft) String() string { return protoimpl.X.MessageStringOf(x) } func (*AccountGroupLeft) ProtoMessage() {} func (x *AccountGroupLeft) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AccountGroupLeft.ProtoReflect.Descriptor instead. func (*AccountGroupLeft) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{21} } func (x *AccountGroupLeft) GetDevicePk() []byte { if x != nil { return x.DevicePk } return nil } func (x *AccountGroupLeft) GetGroupPk() []byte { if x != nil { return x.GroupPk } return nil } // AccountContactRequestDisabled indicates that the account should not be advertised on a public rendezvous point type AccountContactRequestDisabled struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // device_pk is the device sending the event, signs the message DevicePk []byte `protobuf:"bytes,1,opt,name=device_pk,json=devicePk,proto3" json:"device_pk,omitempty"` } func (x *AccountContactRequestDisabled) Reset() { *x = AccountContactRequestDisabled{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *AccountContactRequestDisabled) String() string { return protoimpl.X.MessageStringOf(x) } func (*AccountContactRequestDisabled) ProtoMessage() {} func (x *AccountContactRequestDisabled) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[22] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AccountContactRequestDisabled.ProtoReflect.Descriptor instead. func (*AccountContactRequestDisabled) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{22} } func (x *AccountContactRequestDisabled) GetDevicePk() []byte { if x != nil { return x.DevicePk } return nil } // AccountContactRequestEnabled indicates that the account should be advertised on a public rendezvous point type AccountContactRequestEnabled struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // device_pk is the device sending the event, signs the message DevicePk []byte `protobuf:"bytes,1,opt,name=device_pk,json=devicePk,proto3" json:"device_pk,omitempty"` } func (x *AccountContactRequestEnabled) Reset() { *x = AccountContactRequestEnabled{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *AccountContactRequestEnabled) String() string { return protoimpl.X.MessageStringOf(x) } func (*AccountContactRequestEnabled) ProtoMessage() {} func (x *AccountContactRequestEnabled) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[23] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AccountContactRequestEnabled.ProtoReflect.Descriptor instead. func (*AccountContactRequestEnabled) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{23} } func (x *AccountContactRequestEnabled) GetDevicePk() []byte { if x != nil { return x.DevicePk } return nil } // AccountContactRequestReferenceReset indicates that the account should be advertised on different public rendezvous points type AccountContactRequestReferenceReset struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // device_pk is the device sending the event, signs the message DevicePk []byte `protobuf:"bytes,1,opt,name=device_pk,json=devicePk,proto3" json:"device_pk,omitempty"` // public_rendezvous_seed is the new rendezvous point seed PublicRendezvousSeed []byte `protobuf:"bytes,2,opt,name=public_rendezvous_seed,json=publicRendezvousSeed,proto3" json:"public_rendezvous_seed,omitempty"` } func (x *AccountContactRequestReferenceReset) Reset() { *x = AccountContactRequestReferenceReset{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *AccountContactRequestReferenceReset) String() string { return protoimpl.X.MessageStringOf(x) } func (*AccountContactRequestReferenceReset) ProtoMessage() {} func (x *AccountContactRequestReferenceReset) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[24] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AccountContactRequestReferenceReset.ProtoReflect.Descriptor instead. func (*AccountContactRequestReferenceReset) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{24} } func (x *AccountContactRequestReferenceReset) GetDevicePk() []byte { if x != nil { return x.DevicePk } return nil } func (x *AccountContactRequestReferenceReset) GetPublicRendezvousSeed() []byte { if x != nil { return x.PublicRendezvousSeed } return nil } // This event should be followed by an AccountGroupJoined event // This event should be followed by a GroupMemberDeviceAdded event within the AccountGroup // This event should be followed by a GroupDeviceChainKeyAdded event within the AccountGroup // AccountContactRequestOutgoingEnqueued indicates that the account will attempt to send a contact request when a matching peer is discovered type AccountContactRequestOutgoingEnqueued struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // device_pk is the device sending the event, signs the message DevicePk []byte `protobuf:"bytes,1,opt,name=device_pk,json=devicePk,proto3" json:"device_pk,omitempty"` // group_pk is the 1to1 group with the requested user GroupPk []byte `protobuf:"bytes,2,opt,name=group_pk,json=groupPk,proto3" json:"group_pk,omitempty"` // contact is a message describing how to connect to the other account Contact *ShareableContact `protobuf:"bytes,3,opt,name=contact,proto3" json:"contact,omitempty"` // own_metadata is the identifying metadata that will be shared to the other account OwnMetadata []byte `protobuf:"bytes,4,opt,name=own_metadata,json=ownMetadata,proto3" json:"own_metadata,omitempty"` } func (x *AccountContactRequestOutgoingEnqueued) Reset() { *x = AccountContactRequestOutgoingEnqueued{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *AccountContactRequestOutgoingEnqueued) String() string { return protoimpl.X.MessageStringOf(x) } func (*AccountContactRequestOutgoingEnqueued) ProtoMessage() {} func (x *AccountContactRequestOutgoingEnqueued) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[25] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AccountContactRequestOutgoingEnqueued.ProtoReflect.Descriptor instead. func (*AccountContactRequestOutgoingEnqueued) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{25} } func (x *AccountContactRequestOutgoingEnqueued) GetDevicePk() []byte { if x != nil { return x.DevicePk } return nil } func (x *AccountContactRequestOutgoingEnqueued) GetGroupPk() []byte { if x != nil { return x.GroupPk } return nil } func (x *AccountContactRequestOutgoingEnqueued) GetContact() *ShareableContact { if x != nil { return x.Contact } return nil } func (x *AccountContactRequestOutgoingEnqueued) GetOwnMetadata() []byte { if x != nil { return x.OwnMetadata } return nil } // AccountContactRequestOutgoingSent indicates that the account has sent a contact request type AccountContactRequestOutgoingSent struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // device_pk is the device sending the account event, signs the message DevicePk []byte `protobuf:"bytes,1,opt,name=device_pk,json=devicePk,proto3" json:"device_pk,omitempty"` // contact_pk is the contacted account ContactPk []byte `protobuf:"bytes,2,opt,name=contact_pk,json=contactPk,proto3" json:"contact_pk,omitempty"` } func (x *AccountContactRequestOutgoingSent) Reset() { *x = AccountContactRequestOutgoingSent{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *AccountContactRequestOutgoingSent) String() string { return protoimpl.X.MessageStringOf(x) } func (*AccountContactRequestOutgoingSent) ProtoMessage() {} func (x *AccountContactRequestOutgoingSent) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[26] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AccountContactRequestOutgoingSent.ProtoReflect.Descriptor instead. func (*AccountContactRequestOutgoingSent) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{26} } func (x *AccountContactRequestOutgoingSent) GetDevicePk() []byte { if x != nil { return x.DevicePk } return nil } func (x *AccountContactRequestOutgoingSent) GetContactPk() []byte { if x != nil { return x.ContactPk } return nil } // AccountContactRequestIncomingReceived indicates that the account has received a new contact request type AccountContactRequestIncomingReceived struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // device_pk is the device sending the account event (which received the contact request), signs the message DevicePk []byte `protobuf:"bytes,1,opt,name=device_pk,json=devicePk,proto3" json:"device_pk,omitempty"` // contact_pk is the account sending the request ContactPk []byte `protobuf:"bytes,2,opt,name=contact_pk,json=contactPk,proto3" json:"contact_pk,omitempty"` // TODO: is this necessary? // contact_rendezvous_seed is the rendezvous seed of the contact sending the request ContactRendezvousSeed []byte `protobuf:"bytes,3,opt,name=contact_rendezvous_seed,json=contactRendezvousSeed,proto3" json:"contact_rendezvous_seed,omitempty"` // TODO: is this necessary? // contact_metadata is the metadata specific to the app to identify the contact for the request ContactMetadata []byte `protobuf:"bytes,4,opt,name=contact_metadata,json=contactMetadata,proto3" json:"contact_metadata,omitempty"` } func (x *AccountContactRequestIncomingReceived) Reset() { *x = AccountContactRequestIncomingReceived{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *AccountContactRequestIncomingReceived) String() string { return protoimpl.X.MessageStringOf(x) } func (*AccountContactRequestIncomingReceived) ProtoMessage() {} func (x *AccountContactRequestIncomingReceived) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[27] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AccountContactRequestIncomingReceived.ProtoReflect.Descriptor instead. func (*AccountContactRequestIncomingReceived) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{27} } func (x *AccountContactRequestIncomingReceived) GetDevicePk() []byte { if x != nil { return x.DevicePk } return nil } func (x *AccountContactRequestIncomingReceived) GetContactPk() []byte { if x != nil { return x.ContactPk } return nil } func (x *AccountContactRequestIncomingReceived) GetContactRendezvousSeed() []byte { if x != nil { return x.ContactRendezvousSeed } return nil } func (x *AccountContactRequestIncomingReceived) GetContactMetadata() []byte { if x != nil { return x.ContactMetadata } return nil } // AccountContactRequestIncomingDiscarded indicates that a contact request has been refused type AccountContactRequestIncomingDiscarded struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // device_pk is the device sending the event, signs the message DevicePk []byte `protobuf:"bytes,1,opt,name=device_pk,json=devicePk,proto3" json:"device_pk,omitempty"` // contact_pk is the contact whom request is refused ContactPk []byte `protobuf:"bytes,2,opt,name=contact_pk,json=contactPk,proto3" json:"contact_pk,omitempty"` } func (x *AccountContactRequestIncomingDiscarded) Reset() { *x = AccountContactRequestIncomingDiscarded{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *AccountContactRequestIncomingDiscarded) String() string { return protoimpl.X.MessageStringOf(x) } func (*AccountContactRequestIncomingDiscarded) ProtoMessage() {} func (x *AccountContactRequestIncomingDiscarded) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[28] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AccountContactRequestIncomingDiscarded.ProtoReflect.Descriptor instead. func (*AccountContactRequestIncomingDiscarded) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{28} } func (x *AccountContactRequestIncomingDiscarded) GetDevicePk() []byte { if x != nil { return x.DevicePk } return nil } func (x *AccountContactRequestIncomingDiscarded) GetContactPk() []byte { if x != nil { return x.ContactPk } return nil } // This event should be followed by an AccountGroupJoined event // This event should be followed by GroupMemberDeviceAdded and GroupDeviceChainKeyAdded events within the AccountGroup // AccountContactRequestIncomingAccepted indicates that a contact request has been accepted type AccountContactRequestIncomingAccepted struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // device_pk is the device sending the event, signs the message DevicePk []byte `protobuf:"bytes,1,opt,name=device_pk,json=devicePk,proto3" json:"device_pk,omitempty"` // contact_pk is the contact whom request is accepted ContactPk []byte `protobuf:"bytes,2,opt,name=contact_pk,json=contactPk,proto3" json:"contact_pk,omitempty"` // group_pk is the 1to1 group with the requester user GroupPk []byte `protobuf:"bytes,3,opt,name=group_pk,json=groupPk,proto3" json:"group_pk,omitempty"` } func (x *AccountContactRequestIncomingAccepted) Reset() { *x = AccountContactRequestIncomingAccepted{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *AccountContactRequestIncomingAccepted) String() string { return protoimpl.X.MessageStringOf(x) } func (*AccountContactRequestIncomingAccepted) ProtoMessage() {} func (x *AccountContactRequestIncomingAccepted) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[29] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AccountContactRequestIncomingAccepted.ProtoReflect.Descriptor instead. func (*AccountContactRequestIncomingAccepted) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{29} } func (x *AccountContactRequestIncomingAccepted) GetDevicePk() []byte { if x != nil { return x.DevicePk } return nil } func (x *AccountContactRequestIncomingAccepted) GetContactPk() []byte { if x != nil { return x.ContactPk } return nil } func (x *AccountContactRequestIncomingAccepted) GetGroupPk() []byte { if x != nil { return x.GroupPk } return nil } // AccountContactBlocked indicates that a contact is blocked type AccountContactBlocked struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // device_pk is the device sending the event, signs the message DevicePk []byte `protobuf:"bytes,1,opt,name=device_pk,json=devicePk,proto3" json:"device_pk,omitempty"` // contact_pk is the contact blocked ContactPk []byte `protobuf:"bytes,2,opt,name=contact_pk,json=contactPk,proto3" json:"contact_pk,omitempty"` } func (x *AccountContactBlocked) Reset() { *x = AccountContactBlocked{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *AccountContactBlocked) String() string { return protoimpl.X.MessageStringOf(x) } func (*AccountContactBlocked) ProtoMessage() {} func (x *AccountContactBlocked) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[30] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AccountContactBlocked.ProtoReflect.Descriptor instead. func (*AccountContactBlocked) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{30} } func (x *AccountContactBlocked) GetDevicePk() []byte { if x != nil { return x.DevicePk } return nil } func (x *AccountContactBlocked) GetContactPk() []byte { if x != nil { return x.ContactPk } return nil } // AccountContactUnblocked indicates that a contact is unblocked type AccountContactUnblocked struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // device_pk is the device sending the event, signs the message DevicePk []byte `protobuf:"bytes,1,opt,name=device_pk,json=devicePk,proto3" json:"device_pk,omitempty"` // contact_pk is the contact unblocked ContactPk []byte `protobuf:"bytes,2,opt,name=contact_pk,json=contactPk,proto3" json:"contact_pk,omitempty"` } func (x *AccountContactUnblocked) Reset() { *x = AccountContactUnblocked{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *AccountContactUnblocked) String() string { return protoimpl.X.MessageStringOf(x) } func (*AccountContactUnblocked) ProtoMessage() {} func (x *AccountContactUnblocked) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[31] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AccountContactUnblocked.ProtoReflect.Descriptor instead. func (*AccountContactUnblocked) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{31} } func (x *AccountContactUnblocked) GetDevicePk() []byte { if x != nil { return x.DevicePk } return nil } func (x *AccountContactUnblocked) GetContactPk() []byte { if x != nil { return x.ContactPk } return nil } type GroupReplicating struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // device_pk is the device sending the event, signs the message DevicePk []byte `protobuf:"bytes,1,opt,name=device_pk,json=devicePk,proto3" json:"device_pk,omitempty"` // authentication_url indicates which server has been used for authentication AuthenticationUrl string `protobuf:"bytes,2,opt,name=authentication_url,json=authenticationUrl,proto3" json:"authentication_url,omitempty"` // replication_server indicates which server will be used for replication ReplicationServer string `protobuf:"bytes,3,opt,name=replication_server,json=replicationServer,proto3" json:"replication_server,omitempty"` } func (x *GroupReplicating) Reset() { *x = GroupReplicating{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *GroupReplicating) String() string { return protoimpl.X.MessageStringOf(x) } func (*GroupReplicating) ProtoMessage() {} func (x *GroupReplicating) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[32] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GroupReplicating.ProtoReflect.Descriptor instead. func (*GroupReplicating) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{32} } func (x *GroupReplicating) GetDevicePk() []byte { if x != nil { return x.DevicePk } return nil } func (x *GroupReplicating) GetAuthenticationUrl() string { if x != nil { return x.AuthenticationUrl } return "" } func (x *GroupReplicating) GetReplicationServer() string { if x != nil { return x.ReplicationServer } return "" } type ServiceExportData struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *ServiceExportData) Reset() { *x = ServiceExportData{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[33] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ServiceExportData) String() string { return protoimpl.X.MessageStringOf(x) } func (*ServiceExportData) ProtoMessage() {} func (x *ServiceExportData) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[33] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ServiceExportData.ProtoReflect.Descriptor instead. func (*ServiceExportData) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{33} } type ServiceGetConfiguration struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *ServiceGetConfiguration) Reset() { *x = ServiceGetConfiguration{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[34] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ServiceGetConfiguration) String() string { return protoimpl.X.MessageStringOf(x) } func (*ServiceGetConfiguration) ProtoMessage() {} func (x *ServiceGetConfiguration) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[34] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ServiceGetConfiguration.ProtoReflect.Descriptor instead. func (*ServiceGetConfiguration) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{34} } type ContactRequestReference struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *ContactRequestReference) Reset() { *x = ContactRequestReference{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[35] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ContactRequestReference) String() string { return protoimpl.X.MessageStringOf(x) } func (*ContactRequestReference) ProtoMessage() {} func (x *ContactRequestReference) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[35] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ContactRequestReference.ProtoReflect.Descriptor instead. func (*ContactRequestReference) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{35} } type ContactRequestDisable struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *ContactRequestDisable) Reset() { *x = ContactRequestDisable{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[36] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ContactRequestDisable) String() string { return protoimpl.X.MessageStringOf(x) } func (*ContactRequestDisable) ProtoMessage() {} func (x *ContactRequestDisable) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[36] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ContactRequestDisable.ProtoReflect.Descriptor instead. func (*ContactRequestDisable) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{36} } type ContactRequestEnable struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *ContactRequestEnable) Reset() { *x = ContactRequestEnable{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[37] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ContactRequestEnable) String() string { return protoimpl.X.MessageStringOf(x) } func (*ContactRequestEnable) ProtoMessage() {} func (x *ContactRequestEnable) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[37] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ContactRequestEnable.ProtoReflect.Descriptor instead. func (*ContactRequestEnable) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{37} } type ContactRequestResetReference struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *ContactRequestResetReference) Reset() { *x = ContactRequestResetReference{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[38] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ContactRequestResetReference) String() string { return protoimpl.X.MessageStringOf(x) } func (*ContactRequestResetReference) ProtoMessage() {} func (x *ContactRequestResetReference) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[38] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ContactRequestResetReference.ProtoReflect.Descriptor instead. func (*ContactRequestResetReference) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{38} } type ContactRequestSend struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *ContactRequestSend) Reset() { *x = ContactRequestSend{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[39] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ContactRequestSend) String() string { return protoimpl.X.MessageStringOf(x) } func (*ContactRequestSend) ProtoMessage() {} func (x *ContactRequestSend) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[39] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ContactRequestSend.ProtoReflect.Descriptor instead. func (*ContactRequestSend) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{39} } type ContactRequestAccept struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *ContactRequestAccept) Reset() { *x = ContactRequestAccept{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[40] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ContactRequestAccept) String() string { return protoimpl.X.MessageStringOf(x) } func (*ContactRequestAccept) ProtoMessage() {} func (x *ContactRequestAccept) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[40] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ContactRequestAccept.ProtoReflect.Descriptor instead. func (*ContactRequestAccept) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{40} } type ContactRequestDiscard struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *ContactRequestDiscard) Reset() { *x = ContactRequestDiscard{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[41] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ContactRequestDiscard) String() string { return protoimpl.X.MessageStringOf(x) } func (*ContactRequestDiscard) ProtoMessage() {} func (x *ContactRequestDiscard) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[41] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ContactRequestDiscard.ProtoReflect.Descriptor instead. func (*ContactRequestDiscard) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{41} } type ShareContact struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *ShareContact) Reset() { *x = ShareContact{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[42] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ShareContact) String() string { return protoimpl.X.MessageStringOf(x) } func (*ShareContact) ProtoMessage() {} func (x *ShareContact) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[42] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ShareContact.ProtoReflect.Descriptor instead. func (*ShareContact) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{42} } type DecodeContact struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *DecodeContact) Reset() { *x = DecodeContact{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[43] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *DecodeContact) String() string { return protoimpl.X.MessageStringOf(x) } func (*DecodeContact) ProtoMessage() {} func (x *DecodeContact) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[43] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DecodeContact.ProtoReflect.Descriptor instead. func (*DecodeContact) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{43} } type ContactBlock struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *ContactBlock) Reset() { *x = ContactBlock{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[44] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ContactBlock) String() string { return protoimpl.X.MessageStringOf(x) } func (*ContactBlock) ProtoMessage() {} func (x *ContactBlock) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[44] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ContactBlock.ProtoReflect.Descriptor instead. func (*ContactBlock) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{44} } type ContactUnblock struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *ContactUnblock) Reset() { *x = ContactUnblock{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[45] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ContactUnblock) String() string { return protoimpl.X.MessageStringOf(x) } func (*ContactUnblock) ProtoMessage() {} func (x *ContactUnblock) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[45] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ContactUnblock.ProtoReflect.Descriptor instead. func (*ContactUnblock) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{45} } type ContactAliasKeySend struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *ContactAliasKeySend) Reset() { *x = ContactAliasKeySend{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[46] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ContactAliasKeySend) String() string { return protoimpl.X.MessageStringOf(x) } func (*ContactAliasKeySend) ProtoMessage() {} func (x *ContactAliasKeySend) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[46] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ContactAliasKeySend.ProtoReflect.Descriptor instead. func (*ContactAliasKeySend) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{46} } type MultiMemberGroupCreate struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *MultiMemberGroupCreate) Reset() { *x = MultiMemberGroupCreate{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[47] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *MultiMemberGroupCreate) String() string { return protoimpl.X.MessageStringOf(x) } func (*MultiMemberGroupCreate) ProtoMessage() {} func (x *MultiMemberGroupCreate) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[47] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MultiMemberGroupCreate.ProtoReflect.Descriptor instead. func (*MultiMemberGroupCreate) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{47} } type MultiMemberGroupJoin struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *MultiMemberGroupJoin) Reset() { *x = MultiMemberGroupJoin{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[48] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *MultiMemberGroupJoin) String() string { return protoimpl.X.MessageStringOf(x) } func (*MultiMemberGroupJoin) ProtoMessage() {} func (x *MultiMemberGroupJoin) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[48] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MultiMemberGroupJoin.ProtoReflect.Descriptor instead. func (*MultiMemberGroupJoin) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{48} } type MultiMemberGroupLeave struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *MultiMemberGroupLeave) Reset() { *x = MultiMemberGroupLeave{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[49] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *MultiMemberGroupLeave) String() string { return protoimpl.X.MessageStringOf(x) } func (*MultiMemberGroupLeave) ProtoMessage() {} func (x *MultiMemberGroupLeave) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[49] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MultiMemberGroupLeave.ProtoReflect.Descriptor instead. func (*MultiMemberGroupLeave) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{49} } type MultiMemberGroupAliasResolverDisclose struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *MultiMemberGroupAliasResolverDisclose) Reset() { *x = MultiMemberGroupAliasResolverDisclose{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[50] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *MultiMemberGroupAliasResolverDisclose) String() string { return protoimpl.X.MessageStringOf(x) } func (*MultiMemberGroupAliasResolverDisclose) ProtoMessage() {} func (x *MultiMemberGroupAliasResolverDisclose) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[50] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MultiMemberGroupAliasResolverDisclose.ProtoReflect.Descriptor instead. func (*MultiMemberGroupAliasResolverDisclose) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{50} } type MultiMemberGroupAdminRoleGrant struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *MultiMemberGroupAdminRoleGrant) Reset() { *x = MultiMemberGroupAdminRoleGrant{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[51] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *MultiMemberGroupAdminRoleGrant) String() string { return protoimpl.X.MessageStringOf(x) } func (*MultiMemberGroupAdminRoleGrant) ProtoMessage() {} func (x *MultiMemberGroupAdminRoleGrant) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[51] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MultiMemberGroupAdminRoleGrant.ProtoReflect.Descriptor instead. func (*MultiMemberGroupAdminRoleGrant) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{51} } type MultiMemberGroupInvitationCreate struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *MultiMemberGroupInvitationCreate) Reset() { *x = MultiMemberGroupInvitationCreate{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[52] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *MultiMemberGroupInvitationCreate) String() string { return protoimpl.X.MessageStringOf(x) } func (*MultiMemberGroupInvitationCreate) ProtoMessage() {} func (x *MultiMemberGroupInvitationCreate) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[52] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MultiMemberGroupInvitationCreate.ProtoReflect.Descriptor instead. func (*MultiMemberGroupInvitationCreate) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{52} } type AppMetadataSend struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *AppMetadataSend) Reset() { *x = AppMetadataSend{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[53] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *AppMetadataSend) String() string { return protoimpl.X.MessageStringOf(x) } func (*AppMetadataSend) ProtoMessage() {} func (x *AppMetadataSend) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[53] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AppMetadataSend.ProtoReflect.Descriptor instead. func (*AppMetadataSend) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{53} } type AppMessageSend struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *AppMessageSend) Reset() { *x = AppMessageSend{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[54] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *AppMessageSend) String() string { return protoimpl.X.MessageStringOf(x) } func (*AppMessageSend) ProtoMessage() {} func (x *AppMessageSend) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[54] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AppMessageSend.ProtoReflect.Descriptor instead. func (*AppMessageSend) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{54} } type GroupMetadataEvent struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // event_context contains context information about the event EventContext *EventContext `protobuf:"bytes,1,opt,name=event_context,json=eventContext,proto3" json:"event_context,omitempty"` // metadata contains the newly available metadata Metadata *GroupMetadata `protobuf:"bytes,2,opt,name=metadata,proto3" json:"metadata,omitempty"` // event_clear clear bytes for the event Event []byte `protobuf:"bytes,3,opt,name=event,proto3" json:"event,omitempty"` } func (x *GroupMetadataEvent) Reset() { *x = GroupMetadataEvent{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[55] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *GroupMetadataEvent) String() string { return protoimpl.X.MessageStringOf(x) } func (*GroupMetadataEvent) ProtoMessage() {} func (x *GroupMetadataEvent) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[55] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GroupMetadataEvent.ProtoReflect.Descriptor instead. func (*GroupMetadataEvent) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{55} } func (x *GroupMetadataEvent) GetEventContext() *EventContext { if x != nil { return x.EventContext } return nil } func (x *GroupMetadataEvent) GetMetadata() *GroupMetadata { if x != nil { return x.Metadata } return nil } func (x *GroupMetadataEvent) GetEvent() []byte { if x != nil { return x.Event } return nil } type GroupMessageEvent struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // event_context contains context information about the event EventContext *EventContext `protobuf:"bytes,1,opt,name=event_context,json=eventContext,proto3" json:"event_context,omitempty"` // headers contains headers of the secure message Headers *MessageHeaders `protobuf:"bytes,2,opt,name=headers,proto3" json:"headers,omitempty"` // message contains the secure message payload Message []byte `protobuf:"bytes,3,opt,name=message,proto3" json:"message,omitempty"` } func (x *GroupMessageEvent) Reset() { *x = GroupMessageEvent{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[56] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *GroupMessageEvent) String() string { return protoimpl.X.MessageStringOf(x) } func (*GroupMessageEvent) ProtoMessage() {} func (x *GroupMessageEvent) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[56] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GroupMessageEvent.ProtoReflect.Descriptor instead. func (*GroupMessageEvent) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{56} } func (x *GroupMessageEvent) GetEventContext() *EventContext { if x != nil { return x.EventContext } return nil } func (x *GroupMessageEvent) GetHeaders() *MessageHeaders { if x != nil { return x.Headers } return nil } func (x *GroupMessageEvent) GetMessage() []byte { if x != nil { return x.Message } return nil } type GroupMetadataList struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *GroupMetadataList) Reset() { *x = GroupMetadataList{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[57] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *GroupMetadataList) String() string { return protoimpl.X.MessageStringOf(x) } func (*GroupMetadataList) ProtoMessage() {} func (x *GroupMetadataList) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[57] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GroupMetadataList.ProtoReflect.Descriptor instead. func (*GroupMetadataList) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{57} } type GroupMessageList struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *GroupMessageList) Reset() { *x = GroupMessageList{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[58] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *GroupMessageList) String() string { return protoimpl.X.MessageStringOf(x) } func (*GroupMessageList) ProtoMessage() {} func (x *GroupMessageList) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[58] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GroupMessageList.ProtoReflect.Descriptor instead. func (*GroupMessageList) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{58} } type GroupInfo struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *GroupInfo) Reset() { *x = GroupInfo{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[59] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *GroupInfo) String() string { return protoimpl.X.MessageStringOf(x) } func (*GroupInfo) ProtoMessage() {} func (x *GroupInfo) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[59] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GroupInfo.ProtoReflect.Descriptor instead. func (*GroupInfo) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{59} } type ActivateGroup struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *ActivateGroup) Reset() { *x = ActivateGroup{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[60] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ActivateGroup) String() string { return protoimpl.X.MessageStringOf(x) } func (*ActivateGroup) ProtoMessage() {} func (x *ActivateGroup) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[60] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ActivateGroup.ProtoReflect.Descriptor instead. func (*ActivateGroup) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{60} } type DeactivateGroup struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *DeactivateGroup) Reset() { *x = DeactivateGroup{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[61] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *DeactivateGroup) String() string { return protoimpl.X.MessageStringOf(x) } func (*DeactivateGroup) ProtoMessage() {} func (x *DeactivateGroup) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[61] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DeactivateGroup.ProtoReflect.Descriptor instead. func (*DeactivateGroup) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{61} } type GroupDeviceStatus struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *GroupDeviceStatus) Reset() { *x = GroupDeviceStatus{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[62] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *GroupDeviceStatus) String() string { return protoimpl.X.MessageStringOf(x) } func (*GroupDeviceStatus) ProtoMessage() {} func (x *GroupDeviceStatus) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[62] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GroupDeviceStatus.ProtoReflect.Descriptor instead. func (*GroupDeviceStatus) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{62} } type DebugListGroups struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *DebugListGroups) Reset() { *x = DebugListGroups{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[63] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *DebugListGroups) String() string { return protoimpl.X.MessageStringOf(x) } func (*DebugListGroups) ProtoMessage() {} func (x *DebugListGroups) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[63] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DebugListGroups.ProtoReflect.Descriptor instead. func (*DebugListGroups) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{63} } type DebugInspectGroupStore struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *DebugInspectGroupStore) Reset() { *x = DebugInspectGroupStore{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[64] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *DebugInspectGroupStore) String() string { return protoimpl.X.MessageStringOf(x) } func (*DebugInspectGroupStore) ProtoMessage() {} func (x *DebugInspectGroupStore) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[64] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DebugInspectGroupStore.ProtoReflect.Descriptor instead. func (*DebugInspectGroupStore) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{64} } type DebugGroup struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *DebugGroup) Reset() { *x = DebugGroup{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[65] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *DebugGroup) String() string { return protoimpl.X.MessageStringOf(x) } func (*DebugGroup) ProtoMessage() {} func (x *DebugGroup) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[65] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DebugGroup.ProtoReflect.Descriptor instead. func (*DebugGroup) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{65} } type ShareableContact struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // pk is the account to send a contact request to Pk []byte `protobuf:"bytes,1,opt,name=pk,proto3" json:"pk,omitempty"` // public_rendezvous_seed is the rendezvous seed used by the account to send a contact request to PublicRendezvousSeed []byte `protobuf:"bytes,2,opt,name=public_rendezvous_seed,json=publicRendezvousSeed,proto3" json:"public_rendezvous_seed,omitempty"` // metadata is the metadata specific to the app to identify the contact for the request Metadata []byte `protobuf:"bytes,3,opt,name=metadata,proto3" json:"metadata,omitempty"` } func (x *ShareableContact) Reset() { *x = ShareableContact{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[66] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ShareableContact) String() string { return protoimpl.X.MessageStringOf(x) } func (*ShareableContact) ProtoMessage() {} func (x *ShareableContact) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[66] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ShareableContact.ProtoReflect.Descriptor instead. func (*ShareableContact) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{66} } func (x *ShareableContact) GetPk() []byte { if x != nil { return x.Pk } return nil } func (x *ShareableContact) GetPublicRendezvousSeed() []byte { if x != nil { return x.PublicRendezvousSeed } return nil } func (x *ShareableContact) GetMetadata() []byte { if x != nil { return x.Metadata } return nil } type ServiceTokenSupportedService struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields ServiceType string `protobuf:"bytes,1,opt,name=service_type,json=serviceType,proto3" json:"service_type,omitempty"` ServiceEndpoint string `protobuf:"bytes,2,opt,name=service_endpoint,json=serviceEndpoint,proto3" json:"service_endpoint,omitempty"` } func (x *ServiceTokenSupportedService) Reset() { *x = ServiceTokenSupportedService{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[67] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ServiceTokenSupportedService) String() string { return protoimpl.X.MessageStringOf(x) } func (*ServiceTokenSupportedService) ProtoMessage() {} func (x *ServiceTokenSupportedService) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[67] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ServiceTokenSupportedService.ProtoReflect.Descriptor instead. func (*ServiceTokenSupportedService) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{67} } func (x *ServiceTokenSupportedService) GetServiceType() string { if x != nil { return x.ServiceType } return "" } func (x *ServiceTokenSupportedService) GetServiceEndpoint() string { if x != nil { return x.ServiceEndpoint } return "" } type ServiceToken struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Token string `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"` AuthenticationUrl string `protobuf:"bytes,2,opt,name=authentication_url,json=authenticationUrl,proto3" json:"authentication_url,omitempty"` SupportedServices []*ServiceTokenSupportedService `protobuf:"bytes,3,rep,name=supported_services,json=supportedServices,proto3" json:"supported_services,omitempty"` Expiration int64 `protobuf:"varint,4,opt,name=expiration,proto3" json:"expiration,omitempty"` } func (x *ServiceToken) Reset() { *x = ServiceToken{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[68] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ServiceToken) String() string { return protoimpl.X.MessageStringOf(x) } func (*ServiceToken) ProtoMessage() {} func (x *ServiceToken) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[68] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ServiceToken.ProtoReflect.Descriptor instead. func (*ServiceToken) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{68} } func (x *ServiceToken) GetToken() string { if x != nil { return x.Token } return "" } func (x *ServiceToken) GetAuthenticationUrl() string { if x != nil { return x.AuthenticationUrl } return "" } func (x *ServiceToken) GetSupportedServices() []*ServiceTokenSupportedService { if x != nil { return x.SupportedServices } return nil } func (x *ServiceToken) GetExpiration() int64 { if x != nil { return x.Expiration } return 0 } type CredentialVerificationServiceInitFlow struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *CredentialVerificationServiceInitFlow) Reset() { *x = CredentialVerificationServiceInitFlow{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[69] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *CredentialVerificationServiceInitFlow) String() string { return protoimpl.X.MessageStringOf(x) } func (*CredentialVerificationServiceInitFlow) ProtoMessage() {} func (x *CredentialVerificationServiceInitFlow) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[69] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use CredentialVerificationServiceInitFlow.ProtoReflect.Descriptor instead. func (*CredentialVerificationServiceInitFlow) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{69} } type CredentialVerificationServiceCompleteFlow struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *CredentialVerificationServiceCompleteFlow) Reset() { *x = CredentialVerificationServiceCompleteFlow{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[70] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *CredentialVerificationServiceCompleteFlow) String() string { return protoimpl.X.MessageStringOf(x) } func (*CredentialVerificationServiceCompleteFlow) ProtoMessage() {} func (x *CredentialVerificationServiceCompleteFlow) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[70] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use CredentialVerificationServiceCompleteFlow.ProtoReflect.Descriptor instead. func (*CredentialVerificationServiceCompleteFlow) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{70} } type VerifiedCredentialsList struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *VerifiedCredentialsList) Reset() { *x = VerifiedCredentialsList{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[71] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *VerifiedCredentialsList) String() string { return protoimpl.X.MessageStringOf(x) } func (*VerifiedCredentialsList) ProtoMessage() {} func (x *VerifiedCredentialsList) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[71] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use VerifiedCredentialsList.ProtoReflect.Descriptor instead. func (*VerifiedCredentialsList) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{71} } type ReplicationServiceRegisterGroup struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *ReplicationServiceRegisterGroup) Reset() { *x = ReplicationServiceRegisterGroup{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[72] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ReplicationServiceRegisterGroup) String() string { return protoimpl.X.MessageStringOf(x) } func (*ReplicationServiceRegisterGroup) ProtoMessage() {} func (x *ReplicationServiceRegisterGroup) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[72] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ReplicationServiceRegisterGroup.ProtoReflect.Descriptor instead. func (*ReplicationServiceRegisterGroup) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{72} } type ReplicationServiceReplicateGroup struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *ReplicationServiceReplicateGroup) Reset() { *x = ReplicationServiceReplicateGroup{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[73] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ReplicationServiceReplicateGroup) String() string { return protoimpl.X.MessageStringOf(x) } func (*ReplicationServiceReplicateGroup) ProtoMessage() {} func (x *ReplicationServiceReplicateGroup) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[73] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ReplicationServiceReplicateGroup.ProtoReflect.Descriptor instead. func (*ReplicationServiceReplicateGroup) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{73} } type SystemInfo struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *SystemInfo) Reset() { *x = SystemInfo{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[74] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *SystemInfo) String() string { return protoimpl.X.MessageStringOf(x) } func (*SystemInfo) ProtoMessage() {} func (x *SystemInfo) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[74] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SystemInfo.ProtoReflect.Descriptor instead. func (*SystemInfo) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{74} } type PeerList struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *PeerList) Reset() { *x = PeerList{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[75] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *PeerList) String() string { return protoimpl.X.MessageStringOf(x) } func (*PeerList) ProtoMessage() {} func (x *PeerList) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[75] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use PeerList.ProtoReflect.Descriptor instead. func (*PeerList) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{75} } // Progress define a generic object that can be used to display a progress bar for long-running actions. type Progress struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields State string `protobuf:"bytes,1,opt,name=state,proto3" json:"state,omitempty"` Doing string `protobuf:"bytes,2,opt,name=doing,proto3" json:"doing,omitempty"` Progress float32 `protobuf:"fixed32,3,opt,name=progress,proto3" json:"progress,omitempty"` Completed uint64 `protobuf:"varint,4,opt,name=completed,proto3" json:"completed,omitempty"` Total uint64 `protobuf:"varint,5,opt,name=total,proto3" json:"total,omitempty"` Delay uint64 `protobuf:"varint,6,opt,name=delay,proto3" json:"delay,omitempty"` } func (x *Progress) Reset() { *x = Progress{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[76] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Progress) String() string { return protoimpl.X.MessageStringOf(x) } func (*Progress) ProtoMessage() {} func (x *Progress) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[76] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Progress.ProtoReflect.Descriptor instead. func (*Progress) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{76} } func (x *Progress) GetState() string { if x != nil { return x.State } return "" } func (x *Progress) GetDoing() string { if x != nil { return x.Doing } return "" } func (x *Progress) GetProgress() float32 { if x != nil { return x.Progress } return 0 } func (x *Progress) GetCompleted() uint64 { if x != nil { return x.Completed } return 0 } func (x *Progress) GetTotal() uint64 { if x != nil { return x.Total } return 0 } func (x *Progress) GetDelay() uint64 { if x != nil { return x.Delay } return 0 } type OutOfStoreMessage struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Cid []byte `protobuf:"bytes,1,opt,name=cid,proto3" json:"cid,omitempty"` DevicePk []byte `protobuf:"bytes,2,opt,name=device_pk,json=devicePk,proto3" json:"device_pk,omitempty"` Counter uint64 `protobuf:"fixed64,3,opt,name=counter,proto3" json:"counter,omitempty"` Sig []byte `protobuf:"bytes,4,opt,name=sig,proto3" json:"sig,omitempty"` Flags uint32 `protobuf:"fixed32,5,opt,name=flags,proto3" json:"flags,omitempty"` EncryptedPayload []byte `protobuf:"bytes,6,opt,name=encrypted_payload,json=encryptedPayload,proto3" json:"encrypted_payload,omitempty"` Nonce []byte `protobuf:"bytes,7,opt,name=nonce,proto3" json:"nonce,omitempty"` } func (x *OutOfStoreMessage) Reset() { *x = OutOfStoreMessage{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[77] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *OutOfStoreMessage) String() string { return protoimpl.X.MessageStringOf(x) } func (*OutOfStoreMessage) ProtoMessage() {} func (x *OutOfStoreMessage) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[77] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use OutOfStoreMessage.ProtoReflect.Descriptor instead. func (*OutOfStoreMessage) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{77} } func (x *OutOfStoreMessage) GetCid() []byte { if x != nil { return x.Cid } return nil } func (x *OutOfStoreMessage) GetDevicePk() []byte { if x != nil { return x.DevicePk } return nil } func (x *OutOfStoreMessage) GetCounter() uint64 { if x != nil { return x.Counter } return 0 } func (x *OutOfStoreMessage) GetSig() []byte { if x != nil { return x.Sig } return nil } func (x *OutOfStoreMessage) GetFlags() uint32 { if x != nil { return x.Flags } return 0 } func (x *OutOfStoreMessage) GetEncryptedPayload() []byte { if x != nil { return x.EncryptedPayload } return nil } func (x *OutOfStoreMessage) GetNonce() []byte { if x != nil { return x.Nonce } return nil } type OutOfStoreMessageEnvelope struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Nonce []byte `protobuf:"bytes,1,opt,name=nonce,proto3" json:"nonce,omitempty"` Box []byte `protobuf:"bytes,2,opt,name=box,proto3" json:"box,omitempty"` GroupReference []byte `protobuf:"bytes,3,opt,name=group_reference,json=groupReference,proto3" json:"group_reference,omitempty"` } func (x *OutOfStoreMessageEnvelope) Reset() { *x = OutOfStoreMessageEnvelope{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[78] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *OutOfStoreMessageEnvelope) String() string { return protoimpl.X.MessageStringOf(x) } func (*OutOfStoreMessageEnvelope) ProtoMessage() {} func (x *OutOfStoreMessageEnvelope) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[78] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use OutOfStoreMessageEnvelope.ProtoReflect.Descriptor instead. func (*OutOfStoreMessageEnvelope) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{78} } func (x *OutOfStoreMessageEnvelope) GetNonce() []byte { if x != nil { return x.Nonce } return nil } func (x *OutOfStoreMessageEnvelope) GetBox() []byte { if x != nil { return x.Box } return nil } func (x *OutOfStoreMessageEnvelope) GetGroupReference() []byte { if x != nil { return x.GroupReference } return nil } type OutOfStoreReceive struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *OutOfStoreReceive) Reset() { *x = OutOfStoreReceive{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[79] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *OutOfStoreReceive) String() string { return protoimpl.X.MessageStringOf(x) } func (*OutOfStoreReceive) ProtoMessage() {} func (x *OutOfStoreReceive) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[79] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use OutOfStoreReceive.ProtoReflect.Descriptor instead. func (*OutOfStoreReceive) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{79} } type OutOfStoreSeal struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *OutOfStoreSeal) Reset() { *x = OutOfStoreSeal{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[80] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *OutOfStoreSeal) String() string { return protoimpl.X.MessageStringOf(x) } func (*OutOfStoreSeal) ProtoMessage() {} func (x *OutOfStoreSeal) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[80] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use OutOfStoreSeal.ProtoReflect.Descriptor instead. func (*OutOfStoreSeal) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{80} } type AccountVerifiedCredentialRegistered struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // device_pk is the public key of the device sending the message DevicePk []byte `protobuf:"bytes,1,opt,name=device_pk,json=devicePk,proto3" json:"device_pk,omitempty"` SignedIdentityPublicKey []byte `protobuf:"bytes,2,opt,name=signed_identity_public_key,json=signedIdentityPublicKey,proto3" json:"signed_identity_public_key,omitempty"` VerifiedCredential string `protobuf:"bytes,3,opt,name=verified_credential,json=verifiedCredential,proto3" json:"verified_credential,omitempty"` RegistrationDate int64 `protobuf:"varint,4,opt,name=registration_date,json=registrationDate,proto3" json:"registration_date,omitempty"` ExpirationDate int64 `protobuf:"varint,5,opt,name=expiration_date,json=expirationDate,proto3" json:"expiration_date,omitempty"` Identifier string `protobuf:"bytes,6,opt,name=identifier,proto3" json:"identifier,omitempty"` Issuer string `protobuf:"bytes,7,opt,name=issuer,proto3" json:"issuer,omitempty"` } func (x *AccountVerifiedCredentialRegistered) Reset() { *x = AccountVerifiedCredentialRegistered{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[81] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *AccountVerifiedCredentialRegistered) String() string { return protoimpl.X.MessageStringOf(x) } func (*AccountVerifiedCredentialRegistered) ProtoMessage() {} func (x *AccountVerifiedCredentialRegistered) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[81] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AccountVerifiedCredentialRegistered.ProtoReflect.Descriptor instead. func (*AccountVerifiedCredentialRegistered) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{81} } func (x *AccountVerifiedCredentialRegistered) GetDevicePk() []byte { if x != nil { return x.DevicePk } return nil } func (x *AccountVerifiedCredentialRegistered) GetSignedIdentityPublicKey() []byte { if x != nil { return x.SignedIdentityPublicKey } return nil } func (x *AccountVerifiedCredentialRegistered) GetVerifiedCredential() string { if x != nil { return x.VerifiedCredential } return "" } func (x *AccountVerifiedCredentialRegistered) GetRegistrationDate() int64 { if x != nil { return x.RegistrationDate } return 0 } func (x *AccountVerifiedCredentialRegistered) GetExpirationDate() int64 { if x != nil { return x.ExpirationDate } return 0 } func (x *AccountVerifiedCredentialRegistered) GetIdentifier() string { if x != nil { return x.Identifier } return "" } func (x *AccountVerifiedCredentialRegistered) GetIssuer() string { if x != nil { return x.Issuer } return "" } type FirstLastCounters struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields First uint64 `protobuf:"varint,1,opt,name=first,proto3" json:"first,omitempty"` Last uint64 `protobuf:"varint,2,opt,name=last,proto3" json:"last,omitempty"` } func (x *FirstLastCounters) Reset() { *x = FirstLastCounters{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[82] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *FirstLastCounters) String() string { return protoimpl.X.MessageStringOf(x) } func (*FirstLastCounters) ProtoMessage() {} func (x *FirstLastCounters) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[82] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use FirstLastCounters.ProtoReflect.Descriptor instead. func (*FirstLastCounters) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{82} } func (x *FirstLastCounters) GetFirst() uint64 { if x != nil { return x.First } return 0 } func (x *FirstLastCounters) GetLast() uint64 { if x != nil { return x.Last } return 0 } // OrbitDBMessageHeads is the payload sent on orbitdb to share peer's heads type OrbitDBMessageHeads struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // sealed box should contain encrypted Box SealedBox []byte `protobuf:"bytes,2,opt,name=sealed_box,json=sealedBox,proto3" json:"sealed_box,omitempty"` // current topic used RawRotation []byte `protobuf:"bytes,3,opt,name=raw_rotation,json=rawRotation,proto3" json:"raw_rotation,omitempty"` } func (x *OrbitDBMessageHeads) Reset() { *x = OrbitDBMessageHeads{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[83] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *OrbitDBMessageHeads) String() string { return protoimpl.X.MessageStringOf(x) } func (*OrbitDBMessageHeads) ProtoMessage() {} func (x *OrbitDBMessageHeads) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[83] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use OrbitDBMessageHeads.ProtoReflect.Descriptor instead. func (*OrbitDBMessageHeads) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{83} } func (x *OrbitDBMessageHeads) GetSealedBox() []byte { if x != nil { return x.SealedBox } return nil } func (x *OrbitDBMessageHeads) GetRawRotation() []byte { if x != nil { return x.RawRotation } return nil } type RefreshContactRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *RefreshContactRequest) Reset() { *x = RefreshContactRequest{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[84] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *RefreshContactRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*RefreshContactRequest) ProtoMessage() {} func (x *RefreshContactRequest) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[84] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use RefreshContactRequest.ProtoReflect.Descriptor instead. func (*RefreshContactRequest) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{84} } type ServiceExportData_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *ServiceExportData_Request) Reset() { *x = ServiceExportData_Request{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[86] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ServiceExportData_Request) String() string { return protoimpl.X.MessageStringOf(x) } func (*ServiceExportData_Request) ProtoMessage() {} func (x *ServiceExportData_Request) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[86] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ServiceExportData_Request.ProtoReflect.Descriptor instead. func (*ServiceExportData_Request) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{33, 0} } type ServiceExportData_Reply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields ExportedData []byte `protobuf:"bytes,1,opt,name=exported_data,json=exportedData,proto3" json:"exported_data,omitempty"` } func (x *ServiceExportData_Reply) Reset() { *x = ServiceExportData_Reply{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[87] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ServiceExportData_Reply) String() string { return protoimpl.X.MessageStringOf(x) } func (*ServiceExportData_Reply) ProtoMessage() {} func (x *ServiceExportData_Reply) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[87] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ServiceExportData_Reply.ProtoReflect.Descriptor instead. func (*ServiceExportData_Reply) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{33, 1} } func (x *ServiceExportData_Reply) GetExportedData() []byte { if x != nil { return x.ExportedData } return nil } type ServiceGetConfiguration_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *ServiceGetConfiguration_Request) Reset() { *x = ServiceGetConfiguration_Request{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[88] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ServiceGetConfiguration_Request) String() string { return protoimpl.X.MessageStringOf(x) } func (*ServiceGetConfiguration_Request) ProtoMessage() {} func (x *ServiceGetConfiguration_Request) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[88] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ServiceGetConfiguration_Request.ProtoReflect.Descriptor instead. func (*ServiceGetConfiguration_Request) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{34, 0} } type ServiceGetConfiguration_Reply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // account_pk is the public key of the current account AccountPk []byte `protobuf:"bytes,1,opt,name=account_pk,json=accountPk,proto3" json:"account_pk,omitempty"` // device_pk is the public key of the current device DevicePk []byte `protobuf:"bytes,2,opt,name=device_pk,json=devicePk,proto3" json:"device_pk,omitempty"` // account_group_pk is the public key of the account group AccountGroupPk []byte `protobuf:"bytes,3,opt,name=account_group_pk,json=accountGroupPk,proto3" json:"account_group_pk,omitempty"` // peer_id is the peer ID of the current IPFS node PeerId string `protobuf:"bytes,4,opt,name=peer_id,json=peerId,proto3" json:"peer_id,omitempty"` // listeners is the list of swarm listening addresses of the current IPFS node Listeners []string `protobuf:"bytes,5,rep,name=listeners,proto3" json:"listeners,omitempty"` BleEnabled ServiceGetConfiguration_SettingState `protobuf:"varint,6,opt,name=ble_enabled,json=bleEnabled,proto3,enum=weshnet.protocol.v1.ServiceGetConfiguration_SettingState" json:"ble_enabled,omitempty"` WifiP2PEnabled 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 MdnsEnabled ServiceGetConfiguration_SettingState `protobuf:"varint,8,opt,name=mdns_enabled,json=mdnsEnabled,proto3,enum=weshnet.protocol.v1.ServiceGetConfiguration_SettingState" json:"mdns_enabled,omitempty"` RelayEnabled ServiceGetConfiguration_SettingState `protobuf:"varint,9,opt,name=relay_enabled,json=relayEnabled,proto3,enum=weshnet.protocol.v1.ServiceGetConfiguration_SettingState" json:"relay_enabled,omitempty"` } func (x *ServiceGetConfiguration_Reply) Reset() { *x = ServiceGetConfiguration_Reply{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[89] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ServiceGetConfiguration_Reply) String() string { return protoimpl.X.MessageStringOf(x) } func (*ServiceGetConfiguration_Reply) ProtoMessage() {} func (x *ServiceGetConfiguration_Reply) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[89] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ServiceGetConfiguration_Reply.ProtoReflect.Descriptor instead. func (*ServiceGetConfiguration_Reply) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{34, 1} } func (x *ServiceGetConfiguration_Reply) GetAccountPk() []byte { if x != nil { return x.AccountPk } return nil } func (x *ServiceGetConfiguration_Reply) GetDevicePk() []byte { if x != nil { return x.DevicePk } return nil } func (x *ServiceGetConfiguration_Reply) GetAccountGroupPk() []byte { if x != nil { return x.AccountGroupPk } return nil } func (x *ServiceGetConfiguration_Reply) GetPeerId() string { if x != nil { return x.PeerId } return "" } func (x *ServiceGetConfiguration_Reply) GetListeners() []string { if x != nil { return x.Listeners } return nil } func (x *ServiceGetConfiguration_Reply) GetBleEnabled() ServiceGetConfiguration_SettingState { if x != nil { return x.BleEnabled } return ServiceGetConfiguration_Unknown } func (x *ServiceGetConfiguration_Reply) GetWifiP2PEnabled() ServiceGetConfiguration_SettingState { if x != nil { return x.WifiP2PEnabled } return ServiceGetConfiguration_Unknown } func (x *ServiceGetConfiguration_Reply) GetMdnsEnabled() ServiceGetConfiguration_SettingState { if x != nil { return x.MdnsEnabled } return ServiceGetConfiguration_Unknown } func (x *ServiceGetConfiguration_Reply) GetRelayEnabled() ServiceGetConfiguration_SettingState { if x != nil { return x.RelayEnabled } return ServiceGetConfiguration_Unknown } type ContactRequestReference_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *ContactRequestReference_Request) Reset() { *x = ContactRequestReference_Request{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[90] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ContactRequestReference_Request) String() string { return protoimpl.X.MessageStringOf(x) } func (*ContactRequestReference_Request) ProtoMessage() {} func (x *ContactRequestReference_Request) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[90] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ContactRequestReference_Request.ProtoReflect.Descriptor instead. func (*ContactRequestReference_Request) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{35, 0} } type ContactRequestReference_Reply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // public_rendezvous_seed is the rendezvous seed used by the current account PublicRendezvousSeed []byte `protobuf:"bytes,1,opt,name=public_rendezvous_seed,json=publicRendezvousSeed,proto3" json:"public_rendezvous_seed,omitempty"` // enabled indicates if incoming contact requests are enabled Enabled bool `protobuf:"varint,2,opt,name=enabled,proto3" json:"enabled,omitempty"` } func (x *ContactRequestReference_Reply) Reset() { *x = ContactRequestReference_Reply{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[91] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ContactRequestReference_Reply) String() string { return protoimpl.X.MessageStringOf(x) } func (*ContactRequestReference_Reply) ProtoMessage() {} func (x *ContactRequestReference_Reply) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[91] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ContactRequestReference_Reply.ProtoReflect.Descriptor instead. func (*ContactRequestReference_Reply) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{35, 1} } func (x *ContactRequestReference_Reply) GetPublicRendezvousSeed() []byte { if x != nil { return x.PublicRendezvousSeed } return nil } func (x *ContactRequestReference_Reply) GetEnabled() bool { if x != nil { return x.Enabled } return false } type ContactRequestDisable_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *ContactRequestDisable_Request) Reset() { *x = ContactRequestDisable_Request{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[92] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ContactRequestDisable_Request) String() string { return protoimpl.X.MessageStringOf(x) } func (*ContactRequestDisable_Request) ProtoMessage() {} func (x *ContactRequestDisable_Request) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[92] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ContactRequestDisable_Request.ProtoReflect.Descriptor instead. func (*ContactRequestDisable_Request) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{36, 0} } type ContactRequestDisable_Reply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *ContactRequestDisable_Reply) Reset() { *x = ContactRequestDisable_Reply{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[93] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ContactRequestDisable_Reply) String() string { return protoimpl.X.MessageStringOf(x) } func (*ContactRequestDisable_Reply) ProtoMessage() {} func (x *ContactRequestDisable_Reply) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[93] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ContactRequestDisable_Reply.ProtoReflect.Descriptor instead. func (*ContactRequestDisable_Reply) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{36, 1} } type ContactRequestEnable_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *ContactRequestEnable_Request) Reset() { *x = ContactRequestEnable_Request{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[94] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ContactRequestEnable_Request) String() string { return protoimpl.X.MessageStringOf(x) } func (*ContactRequestEnable_Request) ProtoMessage() {} func (x *ContactRequestEnable_Request) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[94] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ContactRequestEnable_Request.ProtoReflect.Descriptor instead. func (*ContactRequestEnable_Request) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{37, 0} } type ContactRequestEnable_Reply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // public_rendezvous_seed is the rendezvous seed used by the current account PublicRendezvousSeed []byte `protobuf:"bytes,1,opt,name=public_rendezvous_seed,json=publicRendezvousSeed,proto3" json:"public_rendezvous_seed,omitempty"` } func (x *ContactRequestEnable_Reply) Reset() { *x = ContactRequestEnable_Reply{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[95] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ContactRequestEnable_Reply) String() string { return protoimpl.X.MessageStringOf(x) } func (*ContactRequestEnable_Reply) ProtoMessage() {} func (x *ContactRequestEnable_Reply) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[95] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ContactRequestEnable_Reply.ProtoReflect.Descriptor instead. func (*ContactRequestEnable_Reply) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{37, 1} } func (x *ContactRequestEnable_Reply) GetPublicRendezvousSeed() []byte { if x != nil { return x.PublicRendezvousSeed } return nil } type ContactRequestResetReference_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *ContactRequestResetReference_Request) Reset() { *x = ContactRequestResetReference_Request{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[96] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ContactRequestResetReference_Request) String() string { return protoimpl.X.MessageStringOf(x) } func (*ContactRequestResetReference_Request) ProtoMessage() {} func (x *ContactRequestResetReference_Request) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[96] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ContactRequestResetReference_Request.ProtoReflect.Descriptor instead. func (*ContactRequestResetReference_Request) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{38, 0} } type ContactRequestResetReference_Reply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // public_rendezvous_seed is the rendezvous seed used by the current account PublicRendezvousSeed []byte `protobuf:"bytes,1,opt,name=public_rendezvous_seed,json=publicRendezvousSeed,proto3" json:"public_rendezvous_seed,omitempty"` } func (x *ContactRequestResetReference_Reply) Reset() { *x = ContactRequestResetReference_Reply{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[97] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ContactRequestResetReference_Reply) String() string { return protoimpl.X.MessageStringOf(x) } func (*ContactRequestResetReference_Reply) ProtoMessage() {} func (x *ContactRequestResetReference_Reply) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[97] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ContactRequestResetReference_Reply.ProtoReflect.Descriptor instead. func (*ContactRequestResetReference_Reply) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{38, 1} } func (x *ContactRequestResetReference_Reply) GetPublicRendezvousSeed() []byte { if x != nil { return x.PublicRendezvousSeed } return nil } type ContactRequestSend_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // contact is a message describing how to connect to the other account Contact *ShareableContact `protobuf:"bytes,1,opt,name=contact,proto3" json:"contact,omitempty"` // own_metadata is the identifying metadata that will be shared to the other account OwnMetadata []byte `protobuf:"bytes,2,opt,name=own_metadata,json=ownMetadata,proto3" json:"own_metadata,omitempty"` } func (x *ContactRequestSend_Request) Reset() { *x = ContactRequestSend_Request{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[98] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ContactRequestSend_Request) String() string { return protoimpl.X.MessageStringOf(x) } func (*ContactRequestSend_Request) ProtoMessage() {} func (x *ContactRequestSend_Request) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[98] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ContactRequestSend_Request.ProtoReflect.Descriptor instead. func (*ContactRequestSend_Request) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{39, 0} } func (x *ContactRequestSend_Request) GetContact() *ShareableContact { if x != nil { return x.Contact } return nil } func (x *ContactRequestSend_Request) GetOwnMetadata() []byte { if x != nil { return x.OwnMetadata } return nil } type ContactRequestSend_Reply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *ContactRequestSend_Reply) Reset() { *x = ContactRequestSend_Reply{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[99] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ContactRequestSend_Reply) String() string { return protoimpl.X.MessageStringOf(x) } func (*ContactRequestSend_Reply) ProtoMessage() {} func (x *ContactRequestSend_Reply) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[99] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ContactRequestSend_Reply.ProtoReflect.Descriptor instead. func (*ContactRequestSend_Reply) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{39, 1} } type ContactRequestAccept_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // contact_pk is the identifier of the contact to accept the request from ContactPk []byte `protobuf:"bytes,1,opt,name=contact_pk,json=contactPk,proto3" json:"contact_pk,omitempty"` } func (x *ContactRequestAccept_Request) Reset() { *x = ContactRequestAccept_Request{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[100] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ContactRequestAccept_Request) String() string { return protoimpl.X.MessageStringOf(x) } func (*ContactRequestAccept_Request) ProtoMessage() {} func (x *ContactRequestAccept_Request) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[100] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ContactRequestAccept_Request.ProtoReflect.Descriptor instead. func (*ContactRequestAccept_Request) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{40, 0} } func (x *ContactRequestAccept_Request) GetContactPk() []byte { if x != nil { return x.ContactPk } return nil } type ContactRequestAccept_Reply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *ContactRequestAccept_Reply) Reset() { *x = ContactRequestAccept_Reply{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[101] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ContactRequestAccept_Reply) String() string { return protoimpl.X.MessageStringOf(x) } func (*ContactRequestAccept_Reply) ProtoMessage() {} func (x *ContactRequestAccept_Reply) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[101] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ContactRequestAccept_Reply.ProtoReflect.Descriptor instead. func (*ContactRequestAccept_Reply) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{40, 1} } type ContactRequestDiscard_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // contact_pk is the identifier of the contact to ignore the request from ContactPk []byte `protobuf:"bytes,1,opt,name=contact_pk,json=contactPk,proto3" json:"contact_pk,omitempty"` } func (x *ContactRequestDiscard_Request) Reset() { *x = ContactRequestDiscard_Request{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[102] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ContactRequestDiscard_Request) String() string { return protoimpl.X.MessageStringOf(x) } func (*ContactRequestDiscard_Request) ProtoMessage() {} func (x *ContactRequestDiscard_Request) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[102] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ContactRequestDiscard_Request.ProtoReflect.Descriptor instead. func (*ContactRequestDiscard_Request) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{41, 0} } func (x *ContactRequestDiscard_Request) GetContactPk() []byte { if x != nil { return x.ContactPk } return nil } type ContactRequestDiscard_Reply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *ContactRequestDiscard_Reply) Reset() { *x = ContactRequestDiscard_Reply{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[103] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ContactRequestDiscard_Reply) String() string { return protoimpl.X.MessageStringOf(x) } func (*ContactRequestDiscard_Reply) ProtoMessage() {} func (x *ContactRequestDiscard_Reply) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[103] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ContactRequestDiscard_Reply.ProtoReflect.Descriptor instead. func (*ContactRequestDiscard_Reply) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{41, 1} } type ShareContact_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *ShareContact_Request) Reset() { *x = ShareContact_Request{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[104] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ShareContact_Request) String() string { return protoimpl.X.MessageStringOf(x) } func (*ShareContact_Request) ProtoMessage() {} func (x *ShareContact_Request) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[104] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ShareContact_Request.ProtoReflect.Descriptor instead. func (*ShareContact_Request) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{42, 0} } type ShareContact_Reply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // encoded_contact is the Protobuf encoding of the ShareableContact. You can further encode the bytes for sharing, such as base58 or QR code. EncodedContact []byte `protobuf:"bytes,1,opt,name=encoded_contact,json=encodedContact,proto3" json:"encoded_contact,omitempty"` } func (x *ShareContact_Reply) Reset() { *x = ShareContact_Reply{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[105] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ShareContact_Reply) String() string { return protoimpl.X.MessageStringOf(x) } func (*ShareContact_Reply) ProtoMessage() {} func (x *ShareContact_Reply) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[105] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ShareContact_Reply.ProtoReflect.Descriptor instead. func (*ShareContact_Reply) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{42, 1} } func (x *ShareContact_Reply) GetEncodedContact() []byte { if x != nil { return x.EncodedContact } return nil } type DecodeContact_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // encoded_contact is the Protobuf encoding of the shareable contact (as returned by ShareContact). EncodedContact []byte `protobuf:"bytes,1,opt,name=encoded_contact,json=encodedContact,proto3" json:"encoded_contact,omitempty"` } func (x *DecodeContact_Request) Reset() { *x = DecodeContact_Request{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[106] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *DecodeContact_Request) String() string { return protoimpl.X.MessageStringOf(x) } func (*DecodeContact_Request) ProtoMessage() {} func (x *DecodeContact_Request) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[106] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DecodeContact_Request.ProtoReflect.Descriptor instead. func (*DecodeContact_Request) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{43, 0} } func (x *DecodeContact_Request) GetEncodedContact() []byte { if x != nil { return x.EncodedContact } return nil } type DecodeContact_Reply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // shareable_contact is the decoded shareable contact. Contact *ShareableContact `protobuf:"bytes,1,opt,name=contact,proto3" json:"contact,omitempty"` } func (x *DecodeContact_Reply) Reset() { *x = DecodeContact_Reply{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[107] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *DecodeContact_Reply) String() string { return protoimpl.X.MessageStringOf(x) } func (*DecodeContact_Reply) ProtoMessage() {} func (x *DecodeContact_Reply) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[107] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DecodeContact_Reply.ProtoReflect.Descriptor instead. func (*DecodeContact_Reply) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{43, 1} } func (x *DecodeContact_Reply) GetContact() *ShareableContact { if x != nil { return x.Contact } return nil } type ContactBlock_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // contact_pk is the identifier of the contact to block ContactPk []byte `protobuf:"bytes,1,opt,name=contact_pk,json=contactPk,proto3" json:"contact_pk,omitempty"` } func (x *ContactBlock_Request) Reset() { *x = ContactBlock_Request{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[108] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ContactBlock_Request) String() string { return protoimpl.X.MessageStringOf(x) } func (*ContactBlock_Request) ProtoMessage() {} func (x *ContactBlock_Request) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[108] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ContactBlock_Request.ProtoReflect.Descriptor instead. func (*ContactBlock_Request) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{44, 0} } func (x *ContactBlock_Request) GetContactPk() []byte { if x != nil { return x.ContactPk } return nil } type ContactBlock_Reply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *ContactBlock_Reply) Reset() { *x = ContactBlock_Reply{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[109] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ContactBlock_Reply) String() string { return protoimpl.X.MessageStringOf(x) } func (*ContactBlock_Reply) ProtoMessage() {} func (x *ContactBlock_Reply) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[109] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ContactBlock_Reply.ProtoReflect.Descriptor instead. func (*ContactBlock_Reply) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{44, 1} } type ContactUnblock_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // contact_pk is the identifier of the contact to unblock ContactPk []byte `protobuf:"bytes,1,opt,name=contact_pk,json=contactPk,proto3" json:"contact_pk,omitempty"` } func (x *ContactUnblock_Request) Reset() { *x = ContactUnblock_Request{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[110] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ContactUnblock_Request) String() string { return protoimpl.X.MessageStringOf(x) } func (*ContactUnblock_Request) ProtoMessage() {} func (x *ContactUnblock_Request) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[110] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ContactUnblock_Request.ProtoReflect.Descriptor instead. func (*ContactUnblock_Request) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{45, 0} } func (x *ContactUnblock_Request) GetContactPk() []byte { if x != nil { return x.ContactPk } return nil } type ContactUnblock_Reply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *ContactUnblock_Reply) Reset() { *x = ContactUnblock_Reply{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[111] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ContactUnblock_Reply) String() string { return protoimpl.X.MessageStringOf(x) } func (*ContactUnblock_Reply) ProtoMessage() {} func (x *ContactUnblock_Reply) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[111] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ContactUnblock_Reply.ProtoReflect.Descriptor instead. func (*ContactUnblock_Reply) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{45, 1} } type ContactAliasKeySend_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // contact_pk is the identifier of the contact to send the alias public key to GroupPk []byte `protobuf:"bytes,1,opt,name=group_pk,json=groupPk,proto3" json:"group_pk,omitempty"` } func (x *ContactAliasKeySend_Request) Reset() { *x = ContactAliasKeySend_Request{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[112] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ContactAliasKeySend_Request) String() string { return protoimpl.X.MessageStringOf(x) } func (*ContactAliasKeySend_Request) ProtoMessage() {} func (x *ContactAliasKeySend_Request) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[112] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ContactAliasKeySend_Request.ProtoReflect.Descriptor instead. func (*ContactAliasKeySend_Request) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{46, 0} } func (x *ContactAliasKeySend_Request) GetGroupPk() []byte { if x != nil { return x.GroupPk } return nil } type ContactAliasKeySend_Reply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *ContactAliasKeySend_Reply) Reset() { *x = ContactAliasKeySend_Reply{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[113] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ContactAliasKeySend_Reply) String() string { return protoimpl.X.MessageStringOf(x) } func (*ContactAliasKeySend_Reply) ProtoMessage() {} func (x *ContactAliasKeySend_Reply) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[113] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ContactAliasKeySend_Reply.ProtoReflect.Descriptor instead. func (*ContactAliasKeySend_Reply) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{46, 1} } type MultiMemberGroupCreate_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *MultiMemberGroupCreate_Request) Reset() { *x = MultiMemberGroupCreate_Request{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[114] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *MultiMemberGroupCreate_Request) String() string { return protoimpl.X.MessageStringOf(x) } func (*MultiMemberGroupCreate_Request) ProtoMessage() {} func (x *MultiMemberGroupCreate_Request) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[114] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MultiMemberGroupCreate_Request.ProtoReflect.Descriptor instead. func (*MultiMemberGroupCreate_Request) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{47, 0} } type MultiMemberGroupCreate_Reply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // group_pk is the identifier of the newly created group GroupPk []byte `protobuf:"bytes,1,opt,name=group_pk,json=groupPk,proto3" json:"group_pk,omitempty"` } func (x *MultiMemberGroupCreate_Reply) Reset() { *x = MultiMemberGroupCreate_Reply{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[115] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *MultiMemberGroupCreate_Reply) String() string { return protoimpl.X.MessageStringOf(x) } func (*MultiMemberGroupCreate_Reply) ProtoMessage() {} func (x *MultiMemberGroupCreate_Reply) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[115] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MultiMemberGroupCreate_Reply.ProtoReflect.Descriptor instead. func (*MultiMemberGroupCreate_Reply) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{47, 1} } func (x *MultiMemberGroupCreate_Reply) GetGroupPk() []byte { if x != nil { return x.GroupPk } return nil } type MultiMemberGroupJoin_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // group is the information of the group to join Group *Group `protobuf:"bytes,1,opt,name=group,proto3" json:"group,omitempty"` } func (x *MultiMemberGroupJoin_Request) Reset() { *x = MultiMemberGroupJoin_Request{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[116] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *MultiMemberGroupJoin_Request) String() string { return protoimpl.X.MessageStringOf(x) } func (*MultiMemberGroupJoin_Request) ProtoMessage() {} func (x *MultiMemberGroupJoin_Request) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[116] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MultiMemberGroupJoin_Request.ProtoReflect.Descriptor instead. func (*MultiMemberGroupJoin_Request) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{48, 0} } func (x *MultiMemberGroupJoin_Request) GetGroup() *Group { if x != nil { return x.Group } return nil } type MultiMemberGroupJoin_Reply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *MultiMemberGroupJoin_Reply) Reset() { *x = MultiMemberGroupJoin_Reply{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[117] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *MultiMemberGroupJoin_Reply) String() string { return protoimpl.X.MessageStringOf(x) } func (*MultiMemberGroupJoin_Reply) ProtoMessage() {} func (x *MultiMemberGroupJoin_Reply) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[117] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MultiMemberGroupJoin_Reply.ProtoReflect.Descriptor instead. func (*MultiMemberGroupJoin_Reply) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{48, 1} } type MultiMemberGroupLeave_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields GroupPk []byte `protobuf:"bytes,1,opt,name=group_pk,json=groupPk,proto3" json:"group_pk,omitempty"` } func (x *MultiMemberGroupLeave_Request) Reset() { *x = MultiMemberGroupLeave_Request{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[118] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *MultiMemberGroupLeave_Request) String() string { return protoimpl.X.MessageStringOf(x) } func (*MultiMemberGroupLeave_Request) ProtoMessage() {} func (x *MultiMemberGroupLeave_Request) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[118] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MultiMemberGroupLeave_Request.ProtoReflect.Descriptor instead. func (*MultiMemberGroupLeave_Request) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{49, 0} } func (x *MultiMemberGroupLeave_Request) GetGroupPk() []byte { if x != nil { return x.GroupPk } return nil } type MultiMemberGroupLeave_Reply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *MultiMemberGroupLeave_Reply) Reset() { *x = MultiMemberGroupLeave_Reply{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[119] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *MultiMemberGroupLeave_Reply) String() string { return protoimpl.X.MessageStringOf(x) } func (*MultiMemberGroupLeave_Reply) ProtoMessage() {} func (x *MultiMemberGroupLeave_Reply) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[119] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MultiMemberGroupLeave_Reply.ProtoReflect.Descriptor instead. func (*MultiMemberGroupLeave_Reply) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{49, 1} } type MultiMemberGroupAliasResolverDisclose_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // group_pk is the identifier of the group GroupPk []byte `protobuf:"bytes,1,opt,name=group_pk,json=groupPk,proto3" json:"group_pk,omitempty"` } func (x *MultiMemberGroupAliasResolverDisclose_Request) Reset() { *x = MultiMemberGroupAliasResolverDisclose_Request{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[120] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *MultiMemberGroupAliasResolverDisclose_Request) String() string { return protoimpl.X.MessageStringOf(x) } func (*MultiMemberGroupAliasResolverDisclose_Request) ProtoMessage() {} func (x *MultiMemberGroupAliasResolverDisclose_Request) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[120] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MultiMemberGroupAliasResolverDisclose_Request.ProtoReflect.Descriptor instead. func (*MultiMemberGroupAliasResolverDisclose_Request) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{50, 0} } func (x *MultiMemberGroupAliasResolverDisclose_Request) GetGroupPk() []byte { if x != nil { return x.GroupPk } return nil } type MultiMemberGroupAliasResolverDisclose_Reply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *MultiMemberGroupAliasResolverDisclose_Reply) Reset() { *x = MultiMemberGroupAliasResolverDisclose_Reply{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[121] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *MultiMemberGroupAliasResolverDisclose_Reply) String() string { return protoimpl.X.MessageStringOf(x) } func (*MultiMemberGroupAliasResolverDisclose_Reply) ProtoMessage() {} func (x *MultiMemberGroupAliasResolverDisclose_Reply) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[121] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MultiMemberGroupAliasResolverDisclose_Reply.ProtoReflect.Descriptor instead. func (*MultiMemberGroupAliasResolverDisclose_Reply) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{50, 1} } type MultiMemberGroupAdminRoleGrant_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // group_pk is the identifier of the group GroupPk []byte `protobuf:"bytes,1,opt,name=group_pk,json=groupPk,proto3" json:"group_pk,omitempty"` // member_pk is the identifier of the member which will be granted the admin role MemberPk []byte `protobuf:"bytes,2,opt,name=member_pk,json=memberPk,proto3" json:"member_pk,omitempty"` } func (x *MultiMemberGroupAdminRoleGrant_Request) Reset() { *x = MultiMemberGroupAdminRoleGrant_Request{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[122] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *MultiMemberGroupAdminRoleGrant_Request) String() string { return protoimpl.X.MessageStringOf(x) } func (*MultiMemberGroupAdminRoleGrant_Request) ProtoMessage() {} func (x *MultiMemberGroupAdminRoleGrant_Request) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[122] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MultiMemberGroupAdminRoleGrant_Request.ProtoReflect.Descriptor instead. func (*MultiMemberGroupAdminRoleGrant_Request) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{51, 0} } func (x *MultiMemberGroupAdminRoleGrant_Request) GetGroupPk() []byte { if x != nil { return x.GroupPk } return nil } func (x *MultiMemberGroupAdminRoleGrant_Request) GetMemberPk() []byte { if x != nil { return x.MemberPk } return nil } type MultiMemberGroupAdminRoleGrant_Reply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *MultiMemberGroupAdminRoleGrant_Reply) Reset() { *x = MultiMemberGroupAdminRoleGrant_Reply{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[123] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *MultiMemberGroupAdminRoleGrant_Reply) String() string { return protoimpl.X.MessageStringOf(x) } func (*MultiMemberGroupAdminRoleGrant_Reply) ProtoMessage() {} func (x *MultiMemberGroupAdminRoleGrant_Reply) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[123] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MultiMemberGroupAdminRoleGrant_Reply.ProtoReflect.Descriptor instead. func (*MultiMemberGroupAdminRoleGrant_Reply) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{51, 1} } type MultiMemberGroupInvitationCreate_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // group_pk is the identifier of the group GroupPk []byte `protobuf:"bytes,1,opt,name=group_pk,json=groupPk,proto3" json:"group_pk,omitempty"` } func (x *MultiMemberGroupInvitationCreate_Request) Reset() { *x = MultiMemberGroupInvitationCreate_Request{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[124] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *MultiMemberGroupInvitationCreate_Request) String() string { return protoimpl.X.MessageStringOf(x) } func (*MultiMemberGroupInvitationCreate_Request) ProtoMessage() {} func (x *MultiMemberGroupInvitationCreate_Request) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[124] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MultiMemberGroupInvitationCreate_Request.ProtoReflect.Descriptor instead. func (*MultiMemberGroupInvitationCreate_Request) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{52, 0} } func (x *MultiMemberGroupInvitationCreate_Request) GetGroupPk() []byte { if x != nil { return x.GroupPk } return nil } type MultiMemberGroupInvitationCreate_Reply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // group is the invitation to the group Group *Group `protobuf:"bytes,1,opt,name=group,proto3" json:"group,omitempty"` } func (x *MultiMemberGroupInvitationCreate_Reply) Reset() { *x = MultiMemberGroupInvitationCreate_Reply{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[125] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *MultiMemberGroupInvitationCreate_Reply) String() string { return protoimpl.X.MessageStringOf(x) } func (*MultiMemberGroupInvitationCreate_Reply) ProtoMessage() {} func (x *MultiMemberGroupInvitationCreate_Reply) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[125] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MultiMemberGroupInvitationCreate_Reply.ProtoReflect.Descriptor instead. func (*MultiMemberGroupInvitationCreate_Reply) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{52, 1} } func (x *MultiMemberGroupInvitationCreate_Reply) GetGroup() *Group { if x != nil { return x.Group } return nil } type AppMetadataSend_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // group_pk is the identifier of the group GroupPk []byte `protobuf:"bytes,1,opt,name=group_pk,json=groupPk,proto3" json:"group_pk,omitempty"` // payload is the payload to send Payload []byte `protobuf:"bytes,2,opt,name=payload,proto3" json:"payload,omitempty"` } func (x *AppMetadataSend_Request) Reset() { *x = AppMetadataSend_Request{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[126] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *AppMetadataSend_Request) String() string { return protoimpl.X.MessageStringOf(x) } func (*AppMetadataSend_Request) ProtoMessage() {} func (x *AppMetadataSend_Request) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[126] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AppMetadataSend_Request.ProtoReflect.Descriptor instead. func (*AppMetadataSend_Request) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{53, 0} } func (x *AppMetadataSend_Request) GetGroupPk() []byte { if x != nil { return x.GroupPk } return nil } func (x *AppMetadataSend_Request) GetPayload() []byte { if x != nil { return x.Payload } return nil } type AppMetadataSend_Reply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Cid []byte `protobuf:"bytes,1,opt,name=cid,proto3" json:"cid,omitempty"` } func (x *AppMetadataSend_Reply) Reset() { *x = AppMetadataSend_Reply{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[127] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *AppMetadataSend_Reply) String() string { return protoimpl.X.MessageStringOf(x) } func (*AppMetadataSend_Reply) ProtoMessage() {} func (x *AppMetadataSend_Reply) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[127] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AppMetadataSend_Reply.ProtoReflect.Descriptor instead. func (*AppMetadataSend_Reply) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{53, 1} } func (x *AppMetadataSend_Reply) GetCid() []byte { if x != nil { return x.Cid } return nil } type AppMessageSend_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // group_pk is the identifier of the group GroupPk []byte `protobuf:"bytes,1,opt,name=group_pk,json=groupPk,proto3" json:"group_pk,omitempty"` // payload is the payload to send Payload []byte `protobuf:"bytes,2,opt,name=payload,proto3" json:"payload,omitempty"` } func (x *AppMessageSend_Request) Reset() { *x = AppMessageSend_Request{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[128] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *AppMessageSend_Request) String() string { return protoimpl.X.MessageStringOf(x) } func (*AppMessageSend_Request) ProtoMessage() {} func (x *AppMessageSend_Request) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[128] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AppMessageSend_Request.ProtoReflect.Descriptor instead. func (*AppMessageSend_Request) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{54, 0} } func (x *AppMessageSend_Request) GetGroupPk() []byte { if x != nil { return x.GroupPk } return nil } func (x *AppMessageSend_Request) GetPayload() []byte { if x != nil { return x.Payload } return nil } type AppMessageSend_Reply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Cid []byte `protobuf:"bytes,1,opt,name=cid,proto3" json:"cid,omitempty"` } func (x *AppMessageSend_Reply) Reset() { *x = AppMessageSend_Reply{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[129] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *AppMessageSend_Reply) String() string { return protoimpl.X.MessageStringOf(x) } func (*AppMessageSend_Reply) ProtoMessage() {} func (x *AppMessageSend_Reply) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[129] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AppMessageSend_Reply.ProtoReflect.Descriptor instead. func (*AppMessageSend_Reply) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{54, 1} } func (x *AppMessageSend_Reply) GetCid() []byte { if x != nil { return x.Cid } return nil } type GroupMetadataList_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // group_pk is the identifier of the group GroupPk []byte `protobuf:"bytes,1,opt,name=group_pk,json=groupPk,proto3" json:"group_pk,omitempty"` // since is the lower ID bound used to filter events // if not set, will return events since the beginning SinceId []byte `protobuf:"bytes,2,opt,name=since_id,json=sinceId,proto3" json:"since_id,omitempty"` // since_now will list only new event to come // since_id must not be set SinceNow bool `protobuf:"varint,3,opt,name=since_now,json=sinceNow,proto3" json:"since_now,omitempty"` // until is the upper ID bound used to filter events // if not set, will subscribe to new events to come UntilId []byte `protobuf:"bytes,4,opt,name=until_id,json=untilId,proto3" json:"until_id,omitempty"` // until_now will not list new event to come // until_id must not be set UntilNow bool `protobuf:"varint,5,opt,name=until_now,json=untilNow,proto3" json:"until_now,omitempty"` // reverse_order indicates whether the previous events should be returned in // reverse chronological order ReverseOrder bool `protobuf:"varint,6,opt,name=reverse_order,json=reverseOrder,proto3" json:"reverse_order,omitempty"` } func (x *GroupMetadataList_Request) Reset() { *x = GroupMetadataList_Request{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[130] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *GroupMetadataList_Request) String() string { return protoimpl.X.MessageStringOf(x) } func (*GroupMetadataList_Request) ProtoMessage() {} func (x *GroupMetadataList_Request) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[130] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GroupMetadataList_Request.ProtoReflect.Descriptor instead. func (*GroupMetadataList_Request) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{57, 0} } func (x *GroupMetadataList_Request) GetGroupPk() []byte { if x != nil { return x.GroupPk } return nil } func (x *GroupMetadataList_Request) GetSinceId() []byte { if x != nil { return x.SinceId } return nil } func (x *GroupMetadataList_Request) GetSinceNow() bool { if x != nil { return x.SinceNow } return false } func (x *GroupMetadataList_Request) GetUntilId() []byte { if x != nil { return x.UntilId } return nil } func (x *GroupMetadataList_Request) GetUntilNow() bool { if x != nil { return x.UntilNow } return false } func (x *GroupMetadataList_Request) GetReverseOrder() bool { if x != nil { return x.ReverseOrder } return false } type GroupMessageList_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // group_pk is the identifier of the group GroupPk []byte `protobuf:"bytes,1,opt,name=group_pk,json=groupPk,proto3" json:"group_pk,omitempty"` // since is the lower ID bound used to filter events // if not set, will return events since the beginning SinceId []byte `protobuf:"bytes,2,opt,name=since_id,json=sinceId,proto3" json:"since_id,omitempty"` // since_now will list only new event to come // since_id must not be set SinceNow bool `protobuf:"varint,3,opt,name=since_now,json=sinceNow,proto3" json:"since_now,omitempty"` // until is the upper ID bound used to filter events // if not set, will subscribe to new events to come UntilId []byte `protobuf:"bytes,4,opt,name=until_id,json=untilId,proto3" json:"until_id,omitempty"` // until_now will not list new event to come // until_id must not be set UntilNow bool `protobuf:"varint,5,opt,name=until_now,json=untilNow,proto3" json:"until_now,omitempty"` // reverse_order indicates whether the previous events should be returned in // reverse chronological order ReverseOrder bool `protobuf:"varint,6,opt,name=reverse_order,json=reverseOrder,proto3" json:"reverse_order,omitempty"` } func (x *GroupMessageList_Request) Reset() { *x = GroupMessageList_Request{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[131] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *GroupMessageList_Request) String() string { return protoimpl.X.MessageStringOf(x) } func (*GroupMessageList_Request) ProtoMessage() {} func (x *GroupMessageList_Request) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[131] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GroupMessageList_Request.ProtoReflect.Descriptor instead. func (*GroupMessageList_Request) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{58, 0} } func (x *GroupMessageList_Request) GetGroupPk() []byte { if x != nil { return x.GroupPk } return nil } func (x *GroupMessageList_Request) GetSinceId() []byte { if x != nil { return x.SinceId } return nil } func (x *GroupMessageList_Request) GetSinceNow() bool { if x != nil { return x.SinceNow } return false } func (x *GroupMessageList_Request) GetUntilId() []byte { if x != nil { return x.UntilId } return nil } func (x *GroupMessageList_Request) GetUntilNow() bool { if x != nil { return x.UntilNow } return false } func (x *GroupMessageList_Request) GetReverseOrder() bool { if x != nil { return x.ReverseOrder } return false } type GroupInfo_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // group_pk is the identifier of the group GroupPk []byte `protobuf:"bytes,1,opt,name=group_pk,json=groupPk,proto3" json:"group_pk,omitempty"` // contact_pk is the identifier of the contact ContactPk []byte `protobuf:"bytes,2,opt,name=contact_pk,json=contactPk,proto3" json:"contact_pk,omitempty"` } func (x *GroupInfo_Request) Reset() { *x = GroupInfo_Request{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[132] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *GroupInfo_Request) String() string { return protoimpl.X.MessageStringOf(x) } func (*GroupInfo_Request) ProtoMessage() {} func (x *GroupInfo_Request) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[132] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GroupInfo_Request.ProtoReflect.Descriptor instead. func (*GroupInfo_Request) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{59, 0} } func (x *GroupInfo_Request) GetGroupPk() []byte { if x != nil { return x.GroupPk } return nil } func (x *GroupInfo_Request) GetContactPk() []byte { if x != nil { return x.ContactPk } return nil } type GroupInfo_Reply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // group is the group invitation, containing the group pk and its type Group *Group `protobuf:"bytes,1,opt,name=group,proto3" json:"group,omitempty"` // member_pk is the identifier of the current member in the group MemberPk []byte `protobuf:"bytes,2,opt,name=member_pk,json=memberPk,proto3" json:"member_pk,omitempty"` // device_pk is the identifier of the current device in the group DevicePk []byte `protobuf:"bytes,3,opt,name=device_pk,json=devicePk,proto3" json:"device_pk,omitempty"` } func (x *GroupInfo_Reply) Reset() { *x = GroupInfo_Reply{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[133] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *GroupInfo_Reply) String() string { return protoimpl.X.MessageStringOf(x) } func (*GroupInfo_Reply) ProtoMessage() {} func (x *GroupInfo_Reply) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[133] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GroupInfo_Reply.ProtoReflect.Descriptor instead. func (*GroupInfo_Reply) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{59, 1} } func (x *GroupInfo_Reply) GetGroup() *Group { if x != nil { return x.Group } return nil } func (x *GroupInfo_Reply) GetMemberPk() []byte { if x != nil { return x.MemberPk } return nil } func (x *GroupInfo_Reply) GetDevicePk() []byte { if x != nil { return x.DevicePk } return nil } type ActivateGroup_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // group_pk is the identifier of the group GroupPk []byte `protobuf:"bytes,1,opt,name=group_pk,json=groupPk,proto3" json:"group_pk,omitempty"` // local_only will open the group without enabling network interactions // with other members LocalOnly bool `protobuf:"varint,2,opt,name=local_only,json=localOnly,proto3" json:"local_only,omitempty"` } func (x *ActivateGroup_Request) Reset() { *x = ActivateGroup_Request{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[134] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ActivateGroup_Request) String() string { return protoimpl.X.MessageStringOf(x) } func (*ActivateGroup_Request) ProtoMessage() {} func (x *ActivateGroup_Request) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[134] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ActivateGroup_Request.ProtoReflect.Descriptor instead. func (*ActivateGroup_Request) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{60, 0} } func (x *ActivateGroup_Request) GetGroupPk() []byte { if x != nil { return x.GroupPk } return nil } func (x *ActivateGroup_Request) GetLocalOnly() bool { if x != nil { return x.LocalOnly } return false } type ActivateGroup_Reply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *ActivateGroup_Reply) Reset() { *x = ActivateGroup_Reply{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[135] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ActivateGroup_Reply) String() string { return protoimpl.X.MessageStringOf(x) } func (*ActivateGroup_Reply) ProtoMessage() {} func (x *ActivateGroup_Reply) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[135] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ActivateGroup_Reply.ProtoReflect.Descriptor instead. func (*ActivateGroup_Reply) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{60, 1} } type DeactivateGroup_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // group_pk is the identifier of the group GroupPk []byte `protobuf:"bytes,1,opt,name=group_pk,json=groupPk,proto3" json:"group_pk,omitempty"` } func (x *DeactivateGroup_Request) Reset() { *x = DeactivateGroup_Request{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[136] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *DeactivateGroup_Request) String() string { return protoimpl.X.MessageStringOf(x) } func (*DeactivateGroup_Request) ProtoMessage() {} func (x *DeactivateGroup_Request) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[136] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DeactivateGroup_Request.ProtoReflect.Descriptor instead. func (*DeactivateGroup_Request) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{61, 0} } func (x *DeactivateGroup_Request) GetGroupPk() []byte { if x != nil { return x.GroupPk } return nil } type DeactivateGroup_Reply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *DeactivateGroup_Reply) Reset() { *x = DeactivateGroup_Reply{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[137] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *DeactivateGroup_Reply) String() string { return protoimpl.X.MessageStringOf(x) } func (*DeactivateGroup_Reply) ProtoMessage() {} func (x *DeactivateGroup_Reply) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[137] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DeactivateGroup_Reply.ProtoReflect.Descriptor instead. func (*DeactivateGroup_Reply) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{61, 1} } type GroupDeviceStatus_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields GroupPk []byte `protobuf:"bytes,1,opt,name=group_pk,json=groupPk,proto3" json:"group_pk,omitempty"` } func (x *GroupDeviceStatus_Request) Reset() { *x = GroupDeviceStatus_Request{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[138] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *GroupDeviceStatus_Request) String() string { return protoimpl.X.MessageStringOf(x) } func (*GroupDeviceStatus_Request) ProtoMessage() {} func (x *GroupDeviceStatus_Request) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[138] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GroupDeviceStatus_Request.ProtoReflect.Descriptor instead. func (*GroupDeviceStatus_Request) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{62, 0} } func (x *GroupDeviceStatus_Request) GetGroupPk() []byte { if x != nil { return x.GroupPk } return nil } type GroupDeviceStatus_Reply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Type GroupDeviceStatus_Type `protobuf:"varint,1,opt,name=type,proto3,enum=weshnet.protocol.v1.GroupDeviceStatus_Type" json:"type,omitempty"` Event []byte `protobuf:"bytes,2,opt,name=event,proto3" json:"event,omitempty"` } func (x *GroupDeviceStatus_Reply) Reset() { *x = GroupDeviceStatus_Reply{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[139] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *GroupDeviceStatus_Reply) String() string { return protoimpl.X.MessageStringOf(x) } func (*GroupDeviceStatus_Reply) ProtoMessage() {} func (x *GroupDeviceStatus_Reply) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[139] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GroupDeviceStatus_Reply.ProtoReflect.Descriptor instead. func (*GroupDeviceStatus_Reply) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{62, 1} } func (x *GroupDeviceStatus_Reply) GetType() GroupDeviceStatus_Type { if x != nil { return x.Type } return GroupDeviceStatus_TypeUnknown } func (x *GroupDeviceStatus_Reply) GetEvent() []byte { if x != nil { return x.Event } return nil } type GroupDeviceStatus_Reply_PeerConnected struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields PeerId string `protobuf:"bytes,1,opt,name=peer_id,json=peerId,proto3" json:"peer_id,omitempty"` DevicePk []byte `protobuf:"bytes,2,opt,name=device_pk,json=devicePk,proto3" json:"device_pk,omitempty"` Transports []GroupDeviceStatus_Transport `protobuf:"varint,3,rep,packed,name=transports,proto3,enum=weshnet.protocol.v1.GroupDeviceStatus_Transport" json:"transports,omitempty"` Maddrs []string `protobuf:"bytes,4,rep,name=maddrs,proto3" json:"maddrs,omitempty"` } func (x *GroupDeviceStatus_Reply_PeerConnected) Reset() { *x = GroupDeviceStatus_Reply_PeerConnected{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[140] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *GroupDeviceStatus_Reply_PeerConnected) String() string { return protoimpl.X.MessageStringOf(x) } func (*GroupDeviceStatus_Reply_PeerConnected) ProtoMessage() {} func (x *GroupDeviceStatus_Reply_PeerConnected) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[140] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GroupDeviceStatus_Reply_PeerConnected.ProtoReflect.Descriptor instead. func (*GroupDeviceStatus_Reply_PeerConnected) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{62, 1, 0} } func (x *GroupDeviceStatus_Reply_PeerConnected) GetPeerId() string { if x != nil { return x.PeerId } return "" } func (x *GroupDeviceStatus_Reply_PeerConnected) GetDevicePk() []byte { if x != nil { return x.DevicePk } return nil } func (x *GroupDeviceStatus_Reply_PeerConnected) GetTransports() []GroupDeviceStatus_Transport { if x != nil { return x.Transports } return nil } func (x *GroupDeviceStatus_Reply_PeerConnected) GetMaddrs() []string { if x != nil { return x.Maddrs } return nil } type GroupDeviceStatus_Reply_PeerReconnecting struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields PeerId string `protobuf:"bytes,1,opt,name=peer_id,json=peerId,proto3" json:"peer_id,omitempty"` } func (x *GroupDeviceStatus_Reply_PeerReconnecting) Reset() { *x = GroupDeviceStatus_Reply_PeerReconnecting{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[141] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *GroupDeviceStatus_Reply_PeerReconnecting) String() string { return protoimpl.X.MessageStringOf(x) } func (*GroupDeviceStatus_Reply_PeerReconnecting) ProtoMessage() {} func (x *GroupDeviceStatus_Reply_PeerReconnecting) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[141] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GroupDeviceStatus_Reply_PeerReconnecting.ProtoReflect.Descriptor instead. func (*GroupDeviceStatus_Reply_PeerReconnecting) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{62, 1, 1} } func (x *GroupDeviceStatus_Reply_PeerReconnecting) GetPeerId() string { if x != nil { return x.PeerId } return "" } type GroupDeviceStatus_Reply_PeerDisconnected struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields PeerId string `protobuf:"bytes,1,opt,name=peer_id,json=peerId,proto3" json:"peer_id,omitempty"` } func (x *GroupDeviceStatus_Reply_PeerDisconnected) Reset() { *x = GroupDeviceStatus_Reply_PeerDisconnected{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[142] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *GroupDeviceStatus_Reply_PeerDisconnected) String() string { return protoimpl.X.MessageStringOf(x) } func (*GroupDeviceStatus_Reply_PeerDisconnected) ProtoMessage() {} func (x *GroupDeviceStatus_Reply_PeerDisconnected) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[142] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GroupDeviceStatus_Reply_PeerDisconnected.ProtoReflect.Descriptor instead. func (*GroupDeviceStatus_Reply_PeerDisconnected) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{62, 1, 2} } func (x *GroupDeviceStatus_Reply_PeerDisconnected) GetPeerId() string { if x != nil { return x.PeerId } return "" } type DebugListGroups_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *DebugListGroups_Request) Reset() { *x = DebugListGroups_Request{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[143] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *DebugListGroups_Request) String() string { return protoimpl.X.MessageStringOf(x) } func (*DebugListGroups_Request) ProtoMessage() {} func (x *DebugListGroups_Request) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[143] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DebugListGroups_Request.ProtoReflect.Descriptor instead. func (*DebugListGroups_Request) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{63, 0} } type DebugListGroups_Reply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // group_pk is the public key of the group GroupPk []byte `protobuf:"bytes,1,opt,name=group_pk,json=groupPk,proto3" json:"group_pk,omitempty"` // group_type is the type of the group GroupType GroupType `protobuf:"varint,2,opt,name=group_type,json=groupType,proto3,enum=weshnet.protocol.v1.GroupType" json:"group_type,omitempty"` // contact_pk is the contact public key if appropriate ContactPk []byte `protobuf:"bytes,3,opt,name=contact_pk,json=contactPk,proto3" json:"contact_pk,omitempty"` } func (x *DebugListGroups_Reply) Reset() { *x = DebugListGroups_Reply{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[144] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *DebugListGroups_Reply) String() string { return protoimpl.X.MessageStringOf(x) } func (*DebugListGroups_Reply) ProtoMessage() {} func (x *DebugListGroups_Reply) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[144] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DebugListGroups_Reply.ProtoReflect.Descriptor instead. func (*DebugListGroups_Reply) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{63, 1} } func (x *DebugListGroups_Reply) GetGroupPk() []byte { if x != nil { return x.GroupPk } return nil } func (x *DebugListGroups_Reply) GetGroupType() GroupType { if x != nil { return x.GroupType } return GroupType_GroupTypeUndefined } func (x *DebugListGroups_Reply) GetContactPk() []byte { if x != nil { return x.ContactPk } return nil } type DebugInspectGroupStore_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // group_pk is the identifier of the group GroupPk []byte `protobuf:"bytes,1,opt,name=group_pk,json=groupPk,proto3" json:"group_pk,omitempty"` // log_type is the log to inspect LogType DebugInspectGroupLogType `protobuf:"varint,2,opt,name=log_type,json=logType,proto3,enum=weshnet.protocol.v1.DebugInspectGroupLogType" json:"log_type,omitempty"` } func (x *DebugInspectGroupStore_Request) Reset() { *x = DebugInspectGroupStore_Request{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[145] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *DebugInspectGroupStore_Request) String() string { return protoimpl.X.MessageStringOf(x) } func (*DebugInspectGroupStore_Request) ProtoMessage() {} func (x *DebugInspectGroupStore_Request) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[145] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DebugInspectGroupStore_Request.ProtoReflect.Descriptor instead. func (*DebugInspectGroupStore_Request) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{64, 0} } func (x *DebugInspectGroupStore_Request) GetGroupPk() []byte { if x != nil { return x.GroupPk } return nil } func (x *DebugInspectGroupStore_Request) GetLogType() DebugInspectGroupLogType { if x != nil { return x.LogType } return DebugInspectGroupLogType_DebugInspectGroupLogTypeUndefined } type DebugInspectGroupStore_Reply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // cid is the CID of the IPFS log entry Cid []byte `protobuf:"bytes,1,opt,name=cid,proto3" json:"cid,omitempty"` // parent_cids is the list of the parent entries ParentCids [][]byte `protobuf:"bytes,2,rep,name=parent_cids,json=parentCids,proto3" json:"parent_cids,omitempty"` // event_type metadata event type if subscribed to metadata events MetadataEventType EventType `protobuf:"varint,3,opt,name=metadata_event_type,json=metadataEventType,proto3,enum=weshnet.protocol.v1.EventType" json:"metadata_event_type,omitempty"` // device_pk is the public key of the device signing the entry DevicePk []byte `protobuf:"bytes,4,opt,name=device_pk,json=devicePk,proto3" json:"device_pk,omitempty"` // payload is the un encrypted entry payload if available Payload []byte `protobuf:"bytes,6,opt,name=payload,proto3" json:"payload,omitempty"` } func (x *DebugInspectGroupStore_Reply) Reset() { *x = DebugInspectGroupStore_Reply{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[146] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *DebugInspectGroupStore_Reply) String() string { return protoimpl.X.MessageStringOf(x) } func (*DebugInspectGroupStore_Reply) ProtoMessage() {} func (x *DebugInspectGroupStore_Reply) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[146] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DebugInspectGroupStore_Reply.ProtoReflect.Descriptor instead. func (*DebugInspectGroupStore_Reply) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{64, 1} } func (x *DebugInspectGroupStore_Reply) GetCid() []byte { if x != nil { return x.Cid } return nil } func (x *DebugInspectGroupStore_Reply) GetParentCids() [][]byte { if x != nil { return x.ParentCids } return nil } func (x *DebugInspectGroupStore_Reply) GetMetadataEventType() EventType { if x != nil { return x.MetadataEventType } return EventType_EventTypeUndefined } func (x *DebugInspectGroupStore_Reply) GetDevicePk() []byte { if x != nil { return x.DevicePk } return nil } func (x *DebugInspectGroupStore_Reply) GetPayload() []byte { if x != nil { return x.Payload } return nil } type DebugGroup_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // group_pk is the identifier of the group GroupPk []byte `protobuf:"bytes,1,opt,name=group_pk,json=groupPk,proto3" json:"group_pk,omitempty"` } func (x *DebugGroup_Request) Reset() { *x = DebugGroup_Request{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[147] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *DebugGroup_Request) String() string { return protoimpl.X.MessageStringOf(x) } func (*DebugGroup_Request) ProtoMessage() {} func (x *DebugGroup_Request) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[147] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DebugGroup_Request.ProtoReflect.Descriptor instead. func (*DebugGroup_Request) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{65, 0} } func (x *DebugGroup_Request) GetGroupPk() []byte { if x != nil { return x.GroupPk } return nil } type DebugGroup_Reply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // peer_ids is the list of peer ids connected to the same group PeerIds []string `protobuf:"bytes,1,rep,name=peer_ids,json=peerIds,proto3" json:"peer_ids,omitempty"` } func (x *DebugGroup_Reply) Reset() { *x = DebugGroup_Reply{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[148] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *DebugGroup_Reply) String() string { return protoimpl.X.MessageStringOf(x) } func (*DebugGroup_Reply) ProtoMessage() {} func (x *DebugGroup_Reply) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[148] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DebugGroup_Reply.ProtoReflect.Descriptor instead. func (*DebugGroup_Reply) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{65, 1} } func (x *DebugGroup_Reply) GetPeerIds() []string { if x != nil { return x.PeerIds } return nil } type CredentialVerificationServiceInitFlow_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields ServiceUrl string `protobuf:"bytes,1,opt,name=service_url,json=serviceUrl,proto3" json:"service_url,omitempty"` PublicKey []byte `protobuf:"bytes,2,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` Link string `protobuf:"bytes,3,opt,name=link,proto3" json:"link,omitempty"` } func (x *CredentialVerificationServiceInitFlow_Request) Reset() { *x = CredentialVerificationServiceInitFlow_Request{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[149] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *CredentialVerificationServiceInitFlow_Request) String() string { return protoimpl.X.MessageStringOf(x) } func (*CredentialVerificationServiceInitFlow_Request) ProtoMessage() {} func (x *CredentialVerificationServiceInitFlow_Request) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[149] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use CredentialVerificationServiceInitFlow_Request.ProtoReflect.Descriptor instead. func (*CredentialVerificationServiceInitFlow_Request) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{69, 0} } func (x *CredentialVerificationServiceInitFlow_Request) GetServiceUrl() string { if x != nil { return x.ServiceUrl } return "" } func (x *CredentialVerificationServiceInitFlow_Request) GetPublicKey() []byte { if x != nil { return x.PublicKey } return nil } func (x *CredentialVerificationServiceInitFlow_Request) GetLink() string { if x != nil { return x.Link } return "" } type CredentialVerificationServiceInitFlow_Reply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"` SecureUrl bool `protobuf:"varint,2,opt,name=secure_url,json=secureUrl,proto3" json:"secure_url,omitempty"` } func (x *CredentialVerificationServiceInitFlow_Reply) Reset() { *x = CredentialVerificationServiceInitFlow_Reply{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[150] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *CredentialVerificationServiceInitFlow_Reply) String() string { return protoimpl.X.MessageStringOf(x) } func (*CredentialVerificationServiceInitFlow_Reply) ProtoMessage() {} func (x *CredentialVerificationServiceInitFlow_Reply) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[150] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use CredentialVerificationServiceInitFlow_Reply.ProtoReflect.Descriptor instead. func (*CredentialVerificationServiceInitFlow_Reply) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{69, 1} } func (x *CredentialVerificationServiceInitFlow_Reply) GetUrl() string { if x != nil { return x.Url } return "" } func (x *CredentialVerificationServiceInitFlow_Reply) GetSecureUrl() bool { if x != nil { return x.SecureUrl } return false } type CredentialVerificationServiceCompleteFlow_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields CallbackUri string `protobuf:"bytes,1,opt,name=callback_uri,json=callbackUri,proto3" json:"callback_uri,omitempty"` } func (x *CredentialVerificationServiceCompleteFlow_Request) Reset() { *x = CredentialVerificationServiceCompleteFlow_Request{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[151] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *CredentialVerificationServiceCompleteFlow_Request) String() string { return protoimpl.X.MessageStringOf(x) } func (*CredentialVerificationServiceCompleteFlow_Request) ProtoMessage() {} func (x *CredentialVerificationServiceCompleteFlow_Request) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[151] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use CredentialVerificationServiceCompleteFlow_Request.ProtoReflect.Descriptor instead. func (*CredentialVerificationServiceCompleteFlow_Request) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{70, 0} } func (x *CredentialVerificationServiceCompleteFlow_Request) GetCallbackUri() string { if x != nil { return x.CallbackUri } return "" } type CredentialVerificationServiceCompleteFlow_Reply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Identifier string `protobuf:"bytes,1,opt,name=identifier,proto3" json:"identifier,omitempty"` } func (x *CredentialVerificationServiceCompleteFlow_Reply) Reset() { *x = CredentialVerificationServiceCompleteFlow_Reply{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[152] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *CredentialVerificationServiceCompleteFlow_Reply) String() string { return protoimpl.X.MessageStringOf(x) } func (*CredentialVerificationServiceCompleteFlow_Reply) ProtoMessage() {} func (x *CredentialVerificationServiceCompleteFlow_Reply) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[152] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use CredentialVerificationServiceCompleteFlow_Reply.ProtoReflect.Descriptor instead. func (*CredentialVerificationServiceCompleteFlow_Reply) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{70, 1} } func (x *CredentialVerificationServiceCompleteFlow_Reply) GetIdentifier() string { if x != nil { return x.Identifier } return "" } type VerifiedCredentialsList_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields FilterIdentifier string `protobuf:"bytes,1,opt,name=filter_identifier,json=filterIdentifier,proto3" json:"filter_identifier,omitempty"` FilterIssuer string `protobuf:"bytes,2,opt,name=filter_issuer,json=filterIssuer,proto3" json:"filter_issuer,omitempty"` ExcludeExpired bool `protobuf:"varint,3,opt,name=exclude_expired,json=excludeExpired,proto3" json:"exclude_expired,omitempty"` } func (x *VerifiedCredentialsList_Request) Reset() { *x = VerifiedCredentialsList_Request{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[153] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *VerifiedCredentialsList_Request) String() string { return protoimpl.X.MessageStringOf(x) } func (*VerifiedCredentialsList_Request) ProtoMessage() {} func (x *VerifiedCredentialsList_Request) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[153] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use VerifiedCredentialsList_Request.ProtoReflect.Descriptor instead. func (*VerifiedCredentialsList_Request) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{71, 0} } func (x *VerifiedCredentialsList_Request) GetFilterIdentifier() string { if x != nil { return x.FilterIdentifier } return "" } func (x *VerifiedCredentialsList_Request) GetFilterIssuer() string { if x != nil { return x.FilterIssuer } return "" } func (x *VerifiedCredentialsList_Request) GetExcludeExpired() bool { if x != nil { return x.ExcludeExpired } return false } type VerifiedCredentialsList_Reply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Credential *AccountVerifiedCredentialRegistered `protobuf:"bytes,1,opt,name=credential,proto3" json:"credential,omitempty"` } func (x *VerifiedCredentialsList_Reply) Reset() { *x = VerifiedCredentialsList_Reply{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[154] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *VerifiedCredentialsList_Reply) String() string { return protoimpl.X.MessageStringOf(x) } func (*VerifiedCredentialsList_Reply) ProtoMessage() {} func (x *VerifiedCredentialsList_Reply) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[154] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use VerifiedCredentialsList_Reply.ProtoReflect.Descriptor instead. func (*VerifiedCredentialsList_Reply) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{71, 1} } func (x *VerifiedCredentialsList_Reply) GetCredential() *AccountVerifiedCredentialRegistered { if x != nil { return x.Credential } return nil } type ReplicationServiceRegisterGroup_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields GroupPk []byte `protobuf:"bytes,1,opt,name=group_pk,json=groupPk,proto3" json:"group_pk,omitempty"` Token string `protobuf:"bytes,2,opt,name=token,proto3" json:"token,omitempty"` AuthenticationUrl string `protobuf:"bytes,3,opt,name=authentication_url,json=authenticationUrl,proto3" json:"authentication_url,omitempty"` ReplicationServer string `protobuf:"bytes,4,opt,name=replication_server,json=replicationServer,proto3" json:"replication_server,omitempty"` } func (x *ReplicationServiceRegisterGroup_Request) Reset() { *x = ReplicationServiceRegisterGroup_Request{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[155] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ReplicationServiceRegisterGroup_Request) String() string { return protoimpl.X.MessageStringOf(x) } func (*ReplicationServiceRegisterGroup_Request) ProtoMessage() {} func (x *ReplicationServiceRegisterGroup_Request) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[155] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ReplicationServiceRegisterGroup_Request.ProtoReflect.Descriptor instead. func (*ReplicationServiceRegisterGroup_Request) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{72, 0} } func (x *ReplicationServiceRegisterGroup_Request) GetGroupPk() []byte { if x != nil { return x.GroupPk } return nil } func (x *ReplicationServiceRegisterGroup_Request) GetToken() string { if x != nil { return x.Token } return "" } func (x *ReplicationServiceRegisterGroup_Request) GetAuthenticationUrl() string { if x != nil { return x.AuthenticationUrl } return "" } func (x *ReplicationServiceRegisterGroup_Request) GetReplicationServer() string { if x != nil { return x.ReplicationServer } return "" } type ReplicationServiceRegisterGroup_Reply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *ReplicationServiceRegisterGroup_Reply) Reset() { *x = ReplicationServiceRegisterGroup_Reply{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[156] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ReplicationServiceRegisterGroup_Reply) String() string { return protoimpl.X.MessageStringOf(x) } func (*ReplicationServiceRegisterGroup_Reply) ProtoMessage() {} func (x *ReplicationServiceRegisterGroup_Reply) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[156] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ReplicationServiceRegisterGroup_Reply.ProtoReflect.Descriptor instead. func (*ReplicationServiceRegisterGroup_Reply) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{72, 1} } type ReplicationServiceReplicateGroup_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Group *Group `protobuf:"bytes,1,opt,name=group,proto3" json:"group,omitempty"` } func (x *ReplicationServiceReplicateGroup_Request) Reset() { *x = ReplicationServiceReplicateGroup_Request{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[157] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ReplicationServiceReplicateGroup_Request) String() string { return protoimpl.X.MessageStringOf(x) } func (*ReplicationServiceReplicateGroup_Request) ProtoMessage() {} func (x *ReplicationServiceReplicateGroup_Request) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[157] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ReplicationServiceReplicateGroup_Request.ProtoReflect.Descriptor instead. func (*ReplicationServiceReplicateGroup_Request) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{73, 0} } func (x *ReplicationServiceReplicateGroup_Request) GetGroup() *Group { if x != nil { return x.Group } return nil } type ReplicationServiceReplicateGroup_Reply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Ok bool `protobuf:"varint,1,opt,name=ok,proto3" json:"ok,omitempty"` } func (x *ReplicationServiceReplicateGroup_Reply) Reset() { *x = ReplicationServiceReplicateGroup_Reply{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[158] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ReplicationServiceReplicateGroup_Reply) String() string { return protoimpl.X.MessageStringOf(x) } func (*ReplicationServiceReplicateGroup_Reply) ProtoMessage() {} func (x *ReplicationServiceReplicateGroup_Reply) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[158] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ReplicationServiceReplicateGroup_Reply.ProtoReflect.Descriptor instead. func (*ReplicationServiceReplicateGroup_Reply) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{73, 1} } func (x *ReplicationServiceReplicateGroup_Reply) GetOk() bool { if x != nil { return x.Ok } return false } type SystemInfo_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *SystemInfo_Request) Reset() { *x = SystemInfo_Request{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[159] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *SystemInfo_Request) String() string { return protoimpl.X.MessageStringOf(x) } func (*SystemInfo_Request) ProtoMessage() {} func (x *SystemInfo_Request) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[159] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SystemInfo_Request.ProtoReflect.Descriptor instead. func (*SystemInfo_Request) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{74, 0} } type SystemInfo_Reply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Process *SystemInfo_Process `protobuf:"bytes,1,opt,name=process,proto3" json:"process,omitempty"` P2P *SystemInfo_P2P `protobuf:"bytes,2,opt,name=p2p,proto3" json:"p2p,omitempty"` Orbitdb *SystemInfo_OrbitDB `protobuf:"bytes,3,opt,name=orbitdb,proto3" json:"orbitdb,omitempty"` Warns []string `protobuf:"bytes,4,rep,name=warns,proto3" json:"warns,omitempty"` } func (x *SystemInfo_Reply) Reset() { *x = SystemInfo_Reply{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[160] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *SystemInfo_Reply) String() string { return protoimpl.X.MessageStringOf(x) } func (*SystemInfo_Reply) ProtoMessage() {} func (x *SystemInfo_Reply) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[160] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SystemInfo_Reply.ProtoReflect.Descriptor instead. func (*SystemInfo_Reply) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{74, 1} } func (x *SystemInfo_Reply) GetProcess() *SystemInfo_Process { if x != nil { return x.Process } return nil } func (x *SystemInfo_Reply) GetP2P() *SystemInfo_P2P { if x != nil { return x.P2P } return nil } func (x *SystemInfo_Reply) GetOrbitdb() *SystemInfo_OrbitDB { if x != nil { return x.Orbitdb } return nil } func (x *SystemInfo_Reply) GetWarns() []string { if x != nil { return x.Warns } return nil } type SystemInfo_OrbitDB struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields AccountMetadata *SystemInfo_OrbitDB_ReplicationStatus `protobuf:"bytes,1,opt,name=account_metadata,json=accountMetadata,proto3" json:"account_metadata,omitempty"` } func (x *SystemInfo_OrbitDB) Reset() { *x = SystemInfo_OrbitDB{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[161] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *SystemInfo_OrbitDB) String() string { return protoimpl.X.MessageStringOf(x) } func (*SystemInfo_OrbitDB) ProtoMessage() {} func (x *SystemInfo_OrbitDB) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[161] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SystemInfo_OrbitDB.ProtoReflect.Descriptor instead. func (*SystemInfo_OrbitDB) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{74, 2} } func (x *SystemInfo_OrbitDB) GetAccountMetadata() *SystemInfo_OrbitDB_ReplicationStatus { if x != nil { return x.AccountMetadata } return nil } type SystemInfo_P2P struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields ConnectedPeers int64 `protobuf:"varint,1,opt,name=connected_peers,json=connectedPeers,proto3" json:"connected_peers,omitempty"` } func (x *SystemInfo_P2P) Reset() { *x = SystemInfo_P2P{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[162] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *SystemInfo_P2P) String() string { return protoimpl.X.MessageStringOf(x) } func (*SystemInfo_P2P) ProtoMessage() {} func (x *SystemInfo_P2P) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[162] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SystemInfo_P2P.ProtoReflect.Descriptor instead. func (*SystemInfo_P2P) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{74, 3} } func (x *SystemInfo_P2P) GetConnectedPeers() int64 { if x != nil { return x.ConnectedPeers } return 0 } type SystemInfo_Process struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"` VcsRef string `protobuf:"bytes,2,opt,name=vcs_ref,json=vcsRef,proto3" json:"vcs_ref,omitempty"` UptimeMs int64 `protobuf:"varint,3,opt,name=uptime_ms,json=uptimeMs,proto3" json:"uptime_ms,omitempty"` UserCpuTimeMs int64 `protobuf:"varint,10,opt,name=user_cpu_time_ms,json=userCpuTimeMs,proto3" json:"user_cpu_time_ms,omitempty"` SystemCpuTimeMs int64 `protobuf:"varint,11,opt,name=system_cpu_time_ms,json=systemCpuTimeMs,proto3" json:"system_cpu_time_ms,omitempty"` StartedAt int64 `protobuf:"varint,12,opt,name=started_at,json=startedAt,proto3" json:"started_at,omitempty"` RlimitCur uint64 `protobuf:"varint,13,opt,name=rlimit_cur,json=rlimitCur,proto3" json:"rlimit_cur,omitempty"` NumGoroutine int64 `protobuf:"varint,14,opt,name=num_goroutine,json=numGoroutine,proto3" json:"num_goroutine,omitempty"` Nofile int64 `protobuf:"varint,15,opt,name=nofile,proto3" json:"nofile,omitempty"` TooManyOpenFiles bool `protobuf:"varint,16,opt,name=too_many_open_files,json=tooManyOpenFiles,proto3" json:"too_many_open_files,omitempty"` NumCpu int64 `protobuf:"varint,17,opt,name=num_cpu,json=numCpu,proto3" json:"num_cpu,omitempty"` GoVersion string `protobuf:"bytes,18,opt,name=go_version,json=goVersion,proto3" json:"go_version,omitempty"` OperatingSystem string `protobuf:"bytes,19,opt,name=operating_system,json=operatingSystem,proto3" json:"operating_system,omitempty"` HostName string `protobuf:"bytes,20,opt,name=host_name,json=hostName,proto3" json:"host_name,omitempty"` Arch string `protobuf:"bytes,21,opt,name=arch,proto3" json:"arch,omitempty"` RlimitMax uint64 `protobuf:"varint,22,opt,name=rlimit_max,json=rlimitMax,proto3" json:"rlimit_max,omitempty"` Pid int64 `protobuf:"varint,23,opt,name=pid,proto3" json:"pid,omitempty"` Ppid int64 `protobuf:"varint,24,opt,name=ppid,proto3" json:"ppid,omitempty"` Priority int64 `protobuf:"varint,25,opt,name=priority,proto3" json:"priority,omitempty"` Uid int64 `protobuf:"varint,26,opt,name=uid,proto3" json:"uid,omitempty"` WorkingDir string `protobuf:"bytes,27,opt,name=working_dir,json=workingDir,proto3" json:"working_dir,omitempty"` SystemUsername string `protobuf:"bytes,28,opt,name=system_username,json=systemUsername,proto3" json:"system_username,omitempty"` } func (x *SystemInfo_Process) Reset() { *x = SystemInfo_Process{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[163] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *SystemInfo_Process) String() string { return protoimpl.X.MessageStringOf(x) } func (*SystemInfo_Process) ProtoMessage() {} func (x *SystemInfo_Process) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[163] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SystemInfo_Process.ProtoReflect.Descriptor instead. func (*SystemInfo_Process) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{74, 4} } func (x *SystemInfo_Process) GetVersion() string { if x != nil { return x.Version } return "" } func (x *SystemInfo_Process) GetVcsRef() string { if x != nil { return x.VcsRef } return "" } func (x *SystemInfo_Process) GetUptimeMs() int64 { if x != nil { return x.UptimeMs } return 0 } func (x *SystemInfo_Process) GetUserCpuTimeMs() int64 { if x != nil { return x.UserCpuTimeMs } return 0 } func (x *SystemInfo_Process) GetSystemCpuTimeMs() int64 { if x != nil { return x.SystemCpuTimeMs } return 0 } func (x *SystemInfo_Process) GetStartedAt() int64 { if x != nil { return x.StartedAt } return 0 } func (x *SystemInfo_Process) GetRlimitCur() uint64 { if x != nil { return x.RlimitCur } return 0 } func (x *SystemInfo_Process) GetNumGoroutine() int64 { if x != nil { return x.NumGoroutine } return 0 } func (x *SystemInfo_Process) GetNofile() int64 { if x != nil { return x.Nofile } return 0 } func (x *SystemInfo_Process) GetTooManyOpenFiles() bool { if x != nil { return x.TooManyOpenFiles } return false } func (x *SystemInfo_Process) GetNumCpu() int64 { if x != nil { return x.NumCpu } return 0 } func (x *SystemInfo_Process) GetGoVersion() string { if x != nil { return x.GoVersion } return "" } func (x *SystemInfo_Process) GetOperatingSystem() string { if x != nil { return x.OperatingSystem } return "" } func (x *SystemInfo_Process) GetHostName() string { if x != nil { return x.HostName } return "" } func (x *SystemInfo_Process) GetArch() string { if x != nil { return x.Arch } return "" } func (x *SystemInfo_Process) GetRlimitMax() uint64 { if x != nil { return x.RlimitMax } return 0 } func (x *SystemInfo_Process) GetPid() int64 { if x != nil { return x.Pid } return 0 } func (x *SystemInfo_Process) GetPpid() int64 { if x != nil { return x.Ppid } return 0 } func (x *SystemInfo_Process) GetPriority() int64 { if x != nil { return x.Priority } return 0 } func (x *SystemInfo_Process) GetUid() int64 { if x != nil { return x.Uid } return 0 } func (x *SystemInfo_Process) GetWorkingDir() string { if x != nil { return x.WorkingDir } return "" } func (x *SystemInfo_Process) GetSystemUsername() string { if x != nil { return x.SystemUsername } return "" } type SystemInfo_OrbitDB_ReplicationStatus struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Progress int64 `protobuf:"varint,1,opt,name=progress,proto3" json:"progress,omitempty"` Maximum int64 `protobuf:"varint,2,opt,name=maximum,proto3" json:"maximum,omitempty"` Buffered int64 `protobuf:"varint,3,opt,name=buffered,proto3" json:"buffered,omitempty"` Queued int64 `protobuf:"varint,4,opt,name=queued,proto3" json:"queued,omitempty"` } func (x *SystemInfo_OrbitDB_ReplicationStatus) Reset() { *x = SystemInfo_OrbitDB_ReplicationStatus{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[164] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *SystemInfo_OrbitDB_ReplicationStatus) String() string { return protoimpl.X.MessageStringOf(x) } func (*SystemInfo_OrbitDB_ReplicationStatus) ProtoMessage() {} func (x *SystemInfo_OrbitDB_ReplicationStatus) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[164] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SystemInfo_OrbitDB_ReplicationStatus.ProtoReflect.Descriptor instead. func (*SystemInfo_OrbitDB_ReplicationStatus) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{74, 2, 0} } func (x *SystemInfo_OrbitDB_ReplicationStatus) GetProgress() int64 { if x != nil { return x.Progress } return 0 } func (x *SystemInfo_OrbitDB_ReplicationStatus) GetMaximum() int64 { if x != nil { return x.Maximum } return 0 } func (x *SystemInfo_OrbitDB_ReplicationStatus) GetBuffered() int64 { if x != nil { return x.Buffered } return 0 } func (x *SystemInfo_OrbitDB_ReplicationStatus) GetQueued() int64 { if x != nil { return x.Queued } return 0 } type PeerList_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *PeerList_Request) Reset() { *x = PeerList_Request{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[165] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *PeerList_Request) String() string { return protoimpl.X.MessageStringOf(x) } func (*PeerList_Request) ProtoMessage() {} func (x *PeerList_Request) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[165] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use PeerList_Request.ProtoReflect.Descriptor instead. func (*PeerList_Request) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{75, 0} } type PeerList_Reply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Peers []*PeerList_Peer `protobuf:"bytes,1,rep,name=peers,proto3" json:"peers,omitempty"` } func (x *PeerList_Reply) Reset() { *x = PeerList_Reply{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[166] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *PeerList_Reply) String() string { return protoimpl.X.MessageStringOf(x) } func (*PeerList_Reply) ProtoMessage() {} func (x *PeerList_Reply) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[166] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use PeerList_Reply.ProtoReflect.Descriptor instead. func (*PeerList_Reply) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{75, 1} } func (x *PeerList_Reply) GetPeers() []*PeerList_Peer { if x != nil { return x.Peers } return nil } type PeerList_Peer struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // id is the libp2p.PeerID. Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // routes are the list of active and known maddr. Routes []*PeerList_Route `protobuf:"bytes,2,rep,name=routes,proto3" json:"routes,omitempty"` // errors is a list of errors related to the peer. Errors []string `protobuf:"bytes,3,rep,name=errors,proto3" json:"errors,omitempty"` // Features is a list of available features. Features []PeerList_Feature `protobuf:"varint,4,rep,packed,name=features,proto3,enum=weshnet.protocol.v1.PeerList_Feature" json:"features,omitempty"` // MinLatency is the minimum latency across all the peer routes. MinLatency int64 `protobuf:"varint,5,opt,name=min_latency,json=minLatency,proto3" json:"min_latency,omitempty"` // IsActive is true if at least one of the route is active. IsActive bool `protobuf:"varint,6,opt,name=is_active,json=isActive,proto3" json:"is_active,omitempty"` // Direction is the aggregate of all the routes's direction. Direction Direction `protobuf:"varint,7,opt,name=direction,proto3,enum=weshnet.protocol.v1.Direction" json:"direction,omitempty"` } func (x *PeerList_Peer) Reset() { *x = PeerList_Peer{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[167] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *PeerList_Peer) String() string { return protoimpl.X.MessageStringOf(x) } func (*PeerList_Peer) ProtoMessage() {} func (x *PeerList_Peer) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[167] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use PeerList_Peer.ProtoReflect.Descriptor instead. func (*PeerList_Peer) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{75, 2} } func (x *PeerList_Peer) GetId() string { if x != nil { return x.Id } return "" } func (x *PeerList_Peer) GetRoutes() []*PeerList_Route { if x != nil { return x.Routes } return nil } func (x *PeerList_Peer) GetErrors() []string { if x != nil { return x.Errors } return nil } func (x *PeerList_Peer) GetFeatures() []PeerList_Feature { if x != nil { return x.Features } return nil } func (x *PeerList_Peer) GetMinLatency() int64 { if x != nil { return x.MinLatency } return 0 } func (x *PeerList_Peer) GetIsActive() bool { if x != nil { return x.IsActive } return false } func (x *PeerList_Peer) GetDirection() Direction { if x != nil { return x.Direction } return Direction_UnknownDir } type PeerList_Route struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // IsActive indicates whether the address is currently used or just known. IsActive bool `protobuf:"varint,1,opt,name=is_active,json=isActive,proto3" json:"is_active,omitempty"` // Address is the multiaddress via which we are connected with the peer. Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"` // Direction is which way the connection was established. Direction Direction `protobuf:"varint,3,opt,name=direction,proto3,enum=weshnet.protocol.v1.Direction" json:"direction,omitempty"` // Latency is the last known round trip time to the peer in ms. Latency int64 `protobuf:"varint,4,opt,name=latency,proto3" json:"latency,omitempty"` // Streams returns list of streams established with the peer. Streams []*PeerList_Stream `protobuf:"bytes,5,rep,name=streams,proto3" json:"streams,omitempty"` } func (x *PeerList_Route) Reset() { *x = PeerList_Route{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[168] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *PeerList_Route) String() string { return protoimpl.X.MessageStringOf(x) } func (*PeerList_Route) ProtoMessage() {} func (x *PeerList_Route) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[168] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use PeerList_Route.ProtoReflect.Descriptor instead. func (*PeerList_Route) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{75, 3} } func (x *PeerList_Route) GetIsActive() bool { if x != nil { return x.IsActive } return false } func (x *PeerList_Route) GetAddress() string { if x != nil { return x.Address } return "" } func (x *PeerList_Route) GetDirection() Direction { if x != nil { return x.Direction } return Direction_UnknownDir } func (x *PeerList_Route) GetLatency() int64 { if x != nil { return x.Latency } return 0 } func (x *PeerList_Route) GetStreams() []*PeerList_Stream { if x != nil { return x.Streams } return nil } type PeerList_Stream struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // id is an identifier used to write protocol headers in streams. Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` } func (x *PeerList_Stream) Reset() { *x = PeerList_Stream{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[169] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *PeerList_Stream) String() string { return protoimpl.X.MessageStringOf(x) } func (*PeerList_Stream) ProtoMessage() {} func (x *PeerList_Stream) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[169] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use PeerList_Stream.ProtoReflect.Descriptor instead. func (*PeerList_Stream) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{75, 4} } func (x *PeerList_Stream) GetId() string { if x != nil { return x.Id } return "" } type OutOfStoreReceive_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Payload []byte `protobuf:"bytes,1,opt,name=payload,proto3" json:"payload,omitempty"` } func (x *OutOfStoreReceive_Request) Reset() { *x = OutOfStoreReceive_Request{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[170] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *OutOfStoreReceive_Request) String() string { return protoimpl.X.MessageStringOf(x) } func (*OutOfStoreReceive_Request) ProtoMessage() {} func (x *OutOfStoreReceive_Request) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[170] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use OutOfStoreReceive_Request.ProtoReflect.Descriptor instead. func (*OutOfStoreReceive_Request) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{79, 0} } func (x *OutOfStoreReceive_Request) GetPayload() []byte { if x != nil { return x.Payload } return nil } type OutOfStoreReceive_Reply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Message *OutOfStoreMessage `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` Cleartext []byte `protobuf:"bytes,2,opt,name=cleartext,proto3" json:"cleartext,omitempty"` GroupPublicKey []byte `protobuf:"bytes,3,opt,name=group_public_key,json=groupPublicKey,proto3" json:"group_public_key,omitempty"` AlreadyReceived bool `protobuf:"varint,4,opt,name=already_received,json=alreadyReceived,proto3" json:"already_received,omitempty"` } func (x *OutOfStoreReceive_Reply) Reset() { *x = OutOfStoreReceive_Reply{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[171] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *OutOfStoreReceive_Reply) String() string { return protoimpl.X.MessageStringOf(x) } func (*OutOfStoreReceive_Reply) ProtoMessage() {} func (x *OutOfStoreReceive_Reply) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[171] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use OutOfStoreReceive_Reply.ProtoReflect.Descriptor instead. func (*OutOfStoreReceive_Reply) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{79, 1} } func (x *OutOfStoreReceive_Reply) GetMessage() *OutOfStoreMessage { if x != nil { return x.Message } return nil } func (x *OutOfStoreReceive_Reply) GetCleartext() []byte { if x != nil { return x.Cleartext } return nil } func (x *OutOfStoreReceive_Reply) GetGroupPublicKey() []byte { if x != nil { return x.GroupPublicKey } return nil } func (x *OutOfStoreReceive_Reply) GetAlreadyReceived() bool { if x != nil { return x.AlreadyReceived } return false } type OutOfStoreSeal_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Cid []byte `protobuf:"bytes,1,opt,name=cid,proto3" json:"cid,omitempty"` GroupPublicKey []byte `protobuf:"bytes,2,opt,name=group_public_key,json=groupPublicKey,proto3" json:"group_public_key,omitempty"` } func (x *OutOfStoreSeal_Request) Reset() { *x = OutOfStoreSeal_Request{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[172] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *OutOfStoreSeal_Request) String() string { return protoimpl.X.MessageStringOf(x) } func (*OutOfStoreSeal_Request) ProtoMessage() {} func (x *OutOfStoreSeal_Request) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[172] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use OutOfStoreSeal_Request.ProtoReflect.Descriptor instead. func (*OutOfStoreSeal_Request) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{80, 0} } func (x *OutOfStoreSeal_Request) GetCid() []byte { if x != nil { return x.Cid } return nil } func (x *OutOfStoreSeal_Request) GetGroupPublicKey() []byte { if x != nil { return x.GroupPublicKey } return nil } type OutOfStoreSeal_Reply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Encrypted []byte `protobuf:"bytes,1,opt,name=encrypted,proto3" json:"encrypted,omitempty"` } func (x *OutOfStoreSeal_Reply) Reset() { *x = OutOfStoreSeal_Reply{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[173] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *OutOfStoreSeal_Reply) String() string { return protoimpl.X.MessageStringOf(x) } func (*OutOfStoreSeal_Reply) ProtoMessage() {} func (x *OutOfStoreSeal_Reply) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[173] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use OutOfStoreSeal_Reply.ProtoReflect.Descriptor instead. func (*OutOfStoreSeal_Reply) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{80, 1} } func (x *OutOfStoreSeal_Reply) GetEncrypted() []byte { if x != nil { return x.Encrypted } return nil } type OrbitDBMessageHeads_Box struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` Heads []byte `protobuf:"bytes,2,opt,name=heads,proto3" json:"heads,omitempty"` DevicePk []byte `protobuf:"bytes,3,opt,name=device_pk,json=devicePk,proto3" json:"device_pk,omitempty"` PeerId []byte `protobuf:"bytes,4,opt,name=peer_id,json=peerId,proto3" json:"peer_id,omitempty"` } func (x *OrbitDBMessageHeads_Box) Reset() { *x = OrbitDBMessageHeads_Box{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[174] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *OrbitDBMessageHeads_Box) String() string { return protoimpl.X.MessageStringOf(x) } func (*OrbitDBMessageHeads_Box) ProtoMessage() {} func (x *OrbitDBMessageHeads_Box) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[174] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use OrbitDBMessageHeads_Box.ProtoReflect.Descriptor instead. func (*OrbitDBMessageHeads_Box) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{83, 0} } func (x *OrbitDBMessageHeads_Box) GetAddress() string { if x != nil { return x.Address } return "" } func (x *OrbitDBMessageHeads_Box) GetHeads() []byte { if x != nil { return x.Heads } return nil } func (x *OrbitDBMessageHeads_Box) GetDevicePk() []byte { if x != nil { return x.DevicePk } return nil } func (x *OrbitDBMessageHeads_Box) GetPeerId() []byte { if x != nil { return x.PeerId } return nil } type RefreshContactRequest_Peer struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // id is the libp2p.PeerID. Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // list of peers multiaddrs. Addrs []string `protobuf:"bytes,2,rep,name=addrs,proto3" json:"addrs,omitempty"` } func (x *RefreshContactRequest_Peer) Reset() { *x = RefreshContactRequest_Peer{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[175] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *RefreshContactRequest_Peer) String() string { return protoimpl.X.MessageStringOf(x) } func (*RefreshContactRequest_Peer) ProtoMessage() {} func (x *RefreshContactRequest_Peer) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[175] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use RefreshContactRequest_Peer.ProtoReflect.Descriptor instead. func (*RefreshContactRequest_Peer) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{84, 0} } func (x *RefreshContactRequest_Peer) GetId() string { if x != nil { return x.Id } return "" } func (x *RefreshContactRequest_Peer) GetAddrs() []string { if x != nil { return x.Addrs } return nil } type RefreshContactRequest_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields ContactPk []byte `protobuf:"bytes,1,opt,name=contact_pk,json=contactPk,proto3" json:"contact_pk,omitempty"` // timeout in second Timeout int64 `protobuf:"varint,2,opt,name=timeout,proto3" json:"timeout,omitempty"` } func (x *RefreshContactRequest_Request) Reset() { *x = RefreshContactRequest_Request{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[176] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *RefreshContactRequest_Request) String() string { return protoimpl.X.MessageStringOf(x) } func (*RefreshContactRequest_Request) ProtoMessage() {} func (x *RefreshContactRequest_Request) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[176] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use RefreshContactRequest_Request.ProtoReflect.Descriptor instead. func (*RefreshContactRequest_Request) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{84, 1} } func (x *RefreshContactRequest_Request) GetContactPk() []byte { if x != nil { return x.ContactPk } return nil } func (x *RefreshContactRequest_Request) GetTimeout() int64 { if x != nil { return x.Timeout } return 0 } type RefreshContactRequest_Reply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // peers found and successfully connected. PeersFound []*RefreshContactRequest_Peer `protobuf:"bytes,1,rep,name=peers_found,json=peersFound,proto3" json:"peers_found,omitempty"` } func (x *RefreshContactRequest_Reply) Reset() { *x = RefreshContactRequest_Reply{} if protoimpl.UnsafeEnabled { mi := &file_protocoltypes_proto_msgTypes[177] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *RefreshContactRequest_Reply) String() string { return protoimpl.X.MessageStringOf(x) } func (*RefreshContactRequest_Reply) ProtoMessage() {} func (x *RefreshContactRequest_Reply) ProtoReflect() protoreflect.Message { mi := &file_protocoltypes_proto_msgTypes[177] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use RefreshContactRequest_Reply.ProtoReflect.Descriptor instead. func (*RefreshContactRequest_Reply) Descriptor() ([]byte, []int) { return file_protocoltypes_proto_rawDescGZIP(), []int{84, 2} } func (x *RefreshContactRequest_Reply) GetPeersFound() []*RefreshContactRequest_Peer { if x != nil { return x.PeersFound } return nil } var File_protocoltypes_proto protoreflect.FileDescriptor var file_protocoltypes_proto_rawDesc = []byte{ 0x0a, 0x13, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x13, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x22, 0xcd, 0x01, 0x0a, 0x07, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x30, 0x0a, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x2e, 0x0a, 0x13, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x11, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x2a, 0x0a, 0x11, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x5f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x34, 0x0a, 0x16, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x7a, 0x76, 0x6f, 0x75, 0x73, 0x5f, 0x73, 0x65, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x14, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x52, 0x65, 0x6e, 0x64, 0x65, 0x7a, 0x76, 0x6f, 0x75, 0x73, 0x53, 0x65, 0x65, 0x64, 0x22, 0xf4, 0x01, 0x0a, 0x05, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x5f, 0x73, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x53, 0x69, 0x67, 0x12, 0x3d, 0x0a, 0x0a, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x54, 0x79, 0x70, 0x65, 0x52, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x54, 0x79, 0x70, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x69, 0x67, 0x6e, 0x5f, 0x70, 0x75, 0x62, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x73, 0x69, 0x67, 0x6e, 0x50, 0x75, 0x62, 0x12, 0x19, 0x0a, 0x08, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6c, 0x69, 0x6e, 0x6b, 0x4b, 0x65, 0x79, 0x12, 0x20, 0x0a, 0x0c, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x73, 0x69, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x6c, 0x69, 0x6e, 0x6b, 0x4b, 0x65, 0x79, 0x53, 0x69, 0x67, 0x22, 0xc7, 0x01, 0x0a, 0x10, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x48, 0x65, 0x61, 0x64, 0x73, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x69, 0x67, 0x6e, 0x5f, 0x70, 0x75, 0x62, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x73, 0x69, 0x67, 0x6e, 0x50, 0x75, 0x62, 0x12, 0x2e, 0x0a, 0x13, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x73, 0x5f, 0x63, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x11, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x48, 0x65, 0x61, 0x64, 0x73, 0x43, 0x69, 0x64, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x73, 0x5f, 0x63, 0x69, 0x64, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x11, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x48, 0x65, 0x61, 0x64, 0x73, 0x43, 0x69, 0x64, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6c, 0x69, 0x6e, 0x6b, 0x4b, 0x65, 0x79, 0x22, 0xce, 0x01, 0x0a, 0x0d, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x3d, 0x0a, 0x0a, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x73, 0x69, 0x67, 0x12, 0x52, 0x0a, 0x11, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x10, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0x41, 0x0a, 0x0d, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x22, 0xe5, 0x01, 0x0a, 0x0e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x73, 0x69, 0x67, 0x12, 0x4d, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x1a, 0x3b, 0x0a, 0x0d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x18, 0x0a, 0x10, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x22, 0x84, 0x01, 0x0a, 0x10, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x52, 0x0a, 0x11, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x10, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0x70, 0x0a, 0x0f, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x22, 0x5e, 0x0a, 0x0c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x09, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x6b, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x22, 0x51, 0x0a, 0x18, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x65, 0x6e, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6b, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x4e, 0x0a, 0x14, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x41, 0x64, 0x64, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6b, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x5f, 0x70, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x50, 0x6b, 0x22, 0x71, 0x0a, 0x16, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x64, 0x64, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x50, 0x6b, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6b, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x73, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x53, 0x69, 0x67, 0x22, 0x47, 0x0a, 0x0e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x4b, 0x65, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4b, 0x65, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x22, 0x77, 0x0a, 0x18, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x4b, 0x65, 0x79, 0x41, 0x64, 0x64, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6b, 0x12, 0x24, 0x0a, 0x0e, 0x64, 0x65, 0x73, 0x74, 0x5f, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x70, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x64, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x50, 0x6b, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x89, 0x01, 0x0a, 0x22, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x41, 0x64, 0x64, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6b, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x22, 0x6b, 0x0a, 0x20, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6b, 0x12, 0x2a, 0x0a, 0x11, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x65, 0x65, 0x5f, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x70, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x65, 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x50, 0x6b, 0x22, 0x45, 0x0a, 0x26, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x50, 0x6b, 0x22, 0x53, 0x0a, 0x20, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x64, 0x64, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x52, 0x65, 0x6e, 0x64, 0x65, 0x7a, 0x76, 0x6f, 0x75, 0x73, 0x53, 0x65, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x65, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x73, 0x65, 0x65, 0x64, 0x22, 0x56, 0x0a, 0x23, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x52, 0x65, 0x6e, 0x64, 0x65, 0x7a, 0x76, 0x6f, 0x75, 0x73, 0x53, 0x65, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x65, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x73, 0x65, 0x65, 0x64, 0x22, 0x63, 0x0a, 0x12, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4a, 0x6f, 0x69, 0x6e, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6b, 0x12, 0x30, 0x0a, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x22, 0x4a, 0x0a, 0x10, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4c, 0x65, 0x66, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6b, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x6b, 0x22, 0x3c, 0x0a, 0x1d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6b, 0x22, 0x3b, 0x0a, 0x1c, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6b, 0x22, 0x78, 0x0a, 0x23, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x65, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6b, 0x12, 0x34, 0x0a, 0x16, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x7a, 0x76, 0x6f, 0x75, 0x73, 0x5f, 0x73, 0x65, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x14, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x52, 0x65, 0x6e, 0x64, 0x65, 0x7a, 0x76, 0x6f, 0x75, 0x73, 0x53, 0x65, 0x65, 0x64, 0x22, 0xc3, 0x01, 0x0a, 0x25, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x71, 0x75, 0x65, 0x75, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6b, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x6b, 0x12, 0x3f, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x6f, 0x77, 0x6e, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x6f, 0x77, 0x6e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0x5f, 0x0a, 0x21, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x6e, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6b, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x5f, 0x70, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x50, 0x6b, 0x22, 0xc6, 0x01, 0x0a, 0x25, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6b, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x5f, 0x70, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x50, 0x6b, 0x12, 0x36, 0x0a, 0x17, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x5f, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x7a, 0x76, 0x6f, 0x75, 0x73, 0x5f, 0x73, 0x65, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x15, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x6e, 0x64, 0x65, 0x7a, 0x76, 0x6f, 0x75, 0x73, 0x53, 0x65, 0x65, 0x64, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0x64, 0x0a, 0x26, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x44, 0x69, 0x73, 0x63, 0x61, 0x72, 0x64, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6b, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x5f, 0x70, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x50, 0x6b, 0x22, 0x7e, 0x0a, 0x25, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6b, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x5f, 0x70, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x50, 0x6b, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x6b, 0x22, 0x53, 0x0a, 0x15, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6b, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x5f, 0x70, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x50, 0x6b, 0x22, 0x55, 0x0a, 0x17, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x55, 0x6e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6b, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x5f, 0x70, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x50, 0x6b, 0x22, 0x8d, 0x01, 0x0a, 0x10, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6b, 0x12, 0x2d, 0x0a, 0x12, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x72, 0x6c, 0x12, 0x2d, 0x0a, 0x12, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x22, 0x4c, 0x0a, 0x11, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x1a, 0x09, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x22, 0x93, 0x05, 0x0a, 0x17, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x09, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0xa3, 0x04, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x50, 0x6b, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6b, 0x12, 0x28, 0x0a, 0x10, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x6b, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x65, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x73, 0x12, 0x5a, 0x0a, 0x0b, 0x62, 0x6c, 0x65, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x39, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0a, 0x62, 0x6c, 0x65, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x63, 0x0a, 0x10, 0x77, 0x69, 0x66, 0x69, 0x5f, 0x70, 0x32, 0x70, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x39, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0e, 0x77, 0x69, 0x66, 0x69, 0x50, 0x32, 0x70, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x5c, 0x0a, 0x0c, 0x6d, 0x64, 0x6e, 0x73, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x39, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0b, 0x6d, 0x64, 0x6e, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x5e, 0x0a, 0x0d, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x39, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0c, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x47, 0x0a, 0x0c, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x10, 0x02, 0x12, 0x0f, 0x0a, 0x0b, 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x10, 0x03, 0x22, 0x7d, 0x0a, 0x17, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x1a, 0x09, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x57, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x34, 0x0a, 0x16, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x7a, 0x76, 0x6f, 0x75, 0x73, 0x5f, 0x73, 0x65, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x14, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x52, 0x65, 0x6e, 0x64, 0x65, 0x7a, 0x76, 0x6f, 0x75, 0x73, 0x53, 0x65, 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x2b, 0x0a, 0x15, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x1a, 0x09, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x07, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x60, 0x0a, 0x14, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x1a, 0x09, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3d, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x34, 0x0a, 0x16, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x7a, 0x76, 0x6f, 0x75, 0x73, 0x5f, 0x73, 0x65, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x14, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x52, 0x65, 0x6e, 0x64, 0x65, 0x7a, 0x76, 0x6f, 0x75, 0x73, 0x53, 0x65, 0x65, 0x64, 0x22, 0x68, 0x0a, 0x1c, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x65, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x1a, 0x09, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3d, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x34, 0x0a, 0x16, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x7a, 0x76, 0x6f, 0x75, 0x73, 0x5f, 0x73, 0x65, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x14, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x52, 0x65, 0x6e, 0x64, 0x65, 0x7a, 0x76, 0x6f, 0x75, 0x73, 0x53, 0x65, 0x65, 0x64, 0x22, 0x8c, 0x01, 0x0a, 0x12, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x65, 0x6e, 0x64, 0x1a, 0x6d, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x6f, 0x77, 0x6e, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x6f, 0x77, 0x6e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x1a, 0x07, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x49, 0x0a, 0x14, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x1a, 0x28, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x50, 0x6b, 0x1a, 0x07, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x4a, 0x0a, 0x15, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x44, 0x69, 0x73, 0x63, 0x61, 0x72, 0x64, 0x1a, 0x28, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x50, 0x6b, 0x1a, 0x07, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x4b, 0x0a, 0x0c, 0x53, 0x68, 0x61, 0x72, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x1a, 0x09, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x27, 0x0a, 0x0f, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x22, 0x8d, 0x01, 0x0a, 0x0d, 0x44, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x1a, 0x32, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x1a, 0x48, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x3f, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x22, 0x41, 0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x1a, 0x28, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x50, 0x6b, 0x1a, 0x07, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x43, 0x0a, 0x0e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x55, 0x6e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x1a, 0x28, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x50, 0x6b, 0x1a, 0x07, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x44, 0x0a, 0x13, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x53, 0x65, 0x6e, 0x64, 0x1a, 0x24, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x6b, 0x1a, 0x07, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x47, 0x0a, 0x16, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x1a, 0x09, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x6b, 0x22, 0x5c, 0x0a, 0x14, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4a, 0x6f, 0x69, 0x6e, 0x1a, 0x3b, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x30, 0x0a, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x1a, 0x07, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x46, 0x0a, 0x15, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x1a, 0x24, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x6b, 0x1a, 0x07, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x56, 0x0a, 0x25, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x44, 0x69, 0x73, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x1a, 0x24, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x6b, 0x1a, 0x07, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x6c, 0x0a, 0x1e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x1a, 0x41, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x6b, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x70, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x50, 0x6b, 0x1a, 0x07, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x83, 0x01, 0x0a, 0x20, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x1a, 0x24, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x6b, 0x1a, 0x39, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x22, 0x72, 0x0a, 0x0f, 0x41, 0x70, 0x70, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x53, 0x65, 0x6e, 0x64, 0x1a, 0x44, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x6b, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x1a, 0x19, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x63, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x63, 0x69, 0x64, 0x22, 0x71, 0x0a, 0x0e, 0x41, 0x70, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x53, 0x65, 0x6e, 0x64, 0x1a, 0x44, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x6b, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x1a, 0x19, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x63, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x63, 0x69, 0x64, 0x22, 0xb2, 0x01, 0x0a, 0x12, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x46, 0x0a, 0x0d, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52, 0x0c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x3e, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0xb4, 0x01, 0x0a, 0x11, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x46, 0x0a, 0x0d, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52, 0x0c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x3d, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0xcf, 0x01, 0x0a, 0x11, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x4c, 0x69, 0x73, 0x74, 0x1a, 0xb9, 0x01, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x6b, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x5f, 0x6e, 0x6f, 0x77, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x4e, 0x6f, 0x77, 0x12, 0x19, 0x0a, 0x08, 0x75, 0x6e, 0x74, 0x69, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x75, 0x6e, 0x74, 0x69, 0x6c, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x75, 0x6e, 0x74, 0x69, 0x6c, 0x5f, 0x6e, 0x6f, 0x77, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x75, 0x6e, 0x74, 0x69, 0x6c, 0x4e, 0x6f, 0x77, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x22, 0xce, 0x01, 0x0a, 0x10, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x1a, 0xb9, 0x01, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x6b, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x5f, 0x6e, 0x6f, 0x77, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x4e, 0x6f, 0x77, 0x12, 0x19, 0x0a, 0x08, 0x75, 0x6e, 0x74, 0x69, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x75, 0x6e, 0x74, 0x69, 0x6c, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x75, 0x6e, 0x74, 0x69, 0x6c, 0x5f, 0x6e, 0x6f, 0x77, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x75, 0x6e, 0x74, 0x69, 0x6c, 0x4e, 0x6f, 0x77, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x22, 0xc5, 0x01, 0x0a, 0x09, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x1a, 0x43, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x6b, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x5f, 0x70, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x50, 0x6b, 0x1a, 0x73, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x70, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x50, 0x6b, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6b, 0x22, 0x5d, 0x0a, 0x0d, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x1a, 0x43, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x6b, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x4f, 0x6e, 0x6c, 0x79, 0x1a, 0x07, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x40, 0x0a, 0x0f, 0x44, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x1a, 0x24, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x6b, 0x1a, 0x07, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0xd1, 0x04, 0x0a, 0x11, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x1a, 0x24, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x6b, 0x1a, 0xea, 0x02, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x3f, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2b, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x1a, 0xaf, 0x01, 0x0a, 0x0d, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x65, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6b, 0x12, 0x50, 0x0a, 0x0a, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x30, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x0a, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x61, 0x64, 0x64, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x61, 0x64, 0x64, 0x72, 0x73, 0x1a, 0x2b, 0x0a, 0x10, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x65, 0x65, 0x72, 0x49, 0x64, 0x1a, 0x2b, 0x0a, 0x10, 0x50, 0x65, 0x65, 0x72, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x65, 0x65, 0x72, 0x49, 0x64, 0x22, 0x62, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x79, 0x70, 0x65, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12, 0x18, 0x0a, 0x14, 0x54, 0x79, 0x70, 0x65, 0x50, 0x65, 0x65, 0x72, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x10, 0x01, 0x12, 0x15, 0x0a, 0x11, 0x54, 0x79, 0x70, 0x65, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x10, 0x02, 0x12, 0x18, 0x0a, 0x14, 0x54, 0x79, 0x70, 0x65, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6e, 0x67, 0x10, 0x03, 0x22, 0x45, 0x0a, 0x09, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x0e, 0x0a, 0x0a, 0x54, 0x70, 0x74, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x54, 0x70, 0x74, 0x4c, 0x41, 0x4e, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x54, 0x70, 0x74, 0x57, 0x41, 0x4e, 0x10, 0x02, 0x12, 0x10, 0x0a, 0x0c, 0x54, 0x70, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x69, 0x6d, 0x69, 0x74, 0x79, 0x10, 0x03, 0x22, 0x9f, 0x01, 0x0a, 0x0f, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x69, 0x73, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x1a, 0x09, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x80, 0x01, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x6b, 0x12, 0x3d, 0x0a, 0x0a, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x54, 0x79, 0x70, 0x65, 0x52, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x5f, 0x70, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x50, 0x6b, 0x22, 0xcc, 0x02, 0x0a, 0x16, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x1a, 0x6e, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x6b, 0x12, 0x48, 0x0a, 0x08, 0x6c, 0x6f, 0x67, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2d, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4c, 0x6f, 0x67, 0x54, 0x79, 0x70, 0x65, 0x52, 0x07, 0x6c, 0x6f, 0x67, 0x54, 0x79, 0x70, 0x65, 0x1a, 0xc1, 0x01, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x63, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x63, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x43, 0x69, 0x64, 0x73, 0x12, 0x4e, 0x0a, 0x13, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x11, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6b, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x56, 0x0a, 0x0a, 0x44, 0x65, 0x62, 0x75, 0x67, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x1a, 0x24, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x6b, 0x1a, 0x22, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x70, 0x65, 0x65, 0x72, 0x49, 0x64, 0x73, 0x22, 0x74, 0x0a, 0x10, 0x53, 0x68, 0x61, 0x72, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x70, 0x6b, 0x12, 0x34, 0x0a, 0x16, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x7a, 0x76, 0x6f, 0x75, 0x73, 0x5f, 0x73, 0x65, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x14, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x52, 0x65, 0x6e, 0x64, 0x65, 0x7a, 0x76, 0x6f, 0x75, 0x73, 0x53, 0x65, 0x65, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0x6c, 0x0a, 0x1c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x22, 0xd5, 0x01, 0x0a, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x2d, 0x0a, 0x12, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x72, 0x6c, 0x12, 0x60, 0x0a, 0x12, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x11, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xc0, 0x01, 0x0a, 0x25, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x69, 0x74, 0x46, 0x6c, 0x6f, 0x77, 0x1a, 0x5d, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x72, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6c, 0x69, 0x6e, 0x6b, 0x1a, 0x38, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x55, 0x72, 0x6c, 0x22, 0x82, 0x01, 0x0a, 0x29, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x46, 0x6c, 0x6f, 0x77, 0x1a, 0x2c, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x55, 0x72, 0x69, 0x1a, 0x27, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x22, 0x83, 0x02, 0x0a, 0x17, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x1a, 0x84, 0x01, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x11, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x73, 0x73, 0x75, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x49, 0x73, 0x73, 0x75, 0x65, 0x72, 0x12, 0x27, 0x0a, 0x0f, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x64, 0x1a, 0x61, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x58, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x38, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x22, 0xc5, 0x01, 0x0a, 0x1f, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x1a, 0x98, 0x01, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x6b, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x2d, 0x0a, 0x12, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x72, 0x6c, 0x12, 0x2d, 0x0a, 0x12, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x1a, 0x07, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x78, 0x0a, 0x20, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x1a, 0x3b, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x30, 0x0a, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x1a, 0x17, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x6f, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x02, 0x6f, 0x6b, 0x22, 0xc2, 0x09, 0x0a, 0x0a, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x1a, 0x09, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0xda, 0x01, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x41, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x12, 0x35, 0x0a, 0x03, 0x70, 0x32, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x50, 0x32, 0x50, 0x52, 0x03, 0x70, 0x32, 0x70, 0x12, 0x41, 0x0a, 0x07, 0x6f, 0x72, 0x62, 0x69, 0x74, 0x64, 0x62, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x4f, 0x72, 0x62, 0x69, 0x74, 0x44, 0x42, 0x52, 0x07, 0x6f, 0x72, 0x62, 0x69, 0x74, 0x64, 0x62, 0x12, 0x14, 0x0a, 0x05, 0x77, 0x61, 0x72, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x77, 0x61, 0x72, 0x6e, 0x73, 0x1a, 0xee, 0x01, 0x0a, 0x07, 0x4f, 0x72, 0x62, 0x69, 0x74, 0x44, 0x42, 0x12, 0x64, 0x0a, 0x10, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x39, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x4f, 0x72, 0x62, 0x69, 0x74, 0x44, 0x42, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x0f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x1a, 0x7d, 0x0a, 0x11, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x12, 0x1a, 0x0a, 0x08, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x71, 0x75, 0x65, 0x75, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x71, 0x75, 0x65, 0x75, 0x65, 0x64, 0x1a, 0x2e, 0x0a, 0x03, 0x50, 0x32, 0x50, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x73, 0x1a, 0xaa, 0x05, 0x0a, 0x07, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x07, 0x76, 0x63, 0x73, 0x5f, 0x72, 0x65, 0x66, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x76, 0x63, 0x73, 0x52, 0x65, 0x66, 0x12, 0x1b, 0x0a, 0x09, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x4d, 0x73, 0x12, 0x27, 0x0a, 0x10, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x75, 0x73, 0x65, 0x72, 0x43, 0x70, 0x75, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x73, 0x12, 0x2b, 0x0a, 0x12, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x43, 0x70, 0x75, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x63, 0x75, 0x72, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x72, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x43, 0x75, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x6e, 0x75, 0x6d, 0x5f, 0x67, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x6e, 0x75, 0x6d, 0x47, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6e, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6e, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x2d, 0x0a, 0x13, 0x74, 0x6f, 0x6f, 0x5f, 0x6d, 0x61, 0x6e, 0x79, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x74, 0x6f, 0x6f, 0x4d, 0x61, 0x6e, 0x79, 0x4f, 0x70, 0x65, 0x6e, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x70, 0x75, 0x18, 0x11, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6e, 0x75, 0x6d, 0x43, 0x70, 0x75, 0x12, 0x1d, 0x0a, 0x0a, 0x67, 0x6f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x67, 0x6f, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x18, 0x13, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1b, 0x0a, 0x09, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x72, 0x63, 0x68, 0x18, 0x15, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x72, 0x63, 0x68, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x6d, 0x61, 0x78, 0x18, 0x16, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x72, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x4d, 0x61, 0x78, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, 0x64, 0x18, 0x17, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x70, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x70, 0x69, 0x64, 0x18, 0x18, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x70, 0x70, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x19, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x75, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x69, 0x72, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x77, 0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67, 0x44, 0x69, 0x72, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0xeb, 0x05, 0x0a, 0x08, 0x50, 0x65, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x1a, 0x09, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x41, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x38, 0x0a, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x52, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x1a, 0xaa, 0x02, 0x0a, 0x04, 0x50, 0x65, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x3b, 0x0a, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x12, 0x41, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x25, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x69, 0x6e, 0x5f, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x6d, 0x69, 0x6e, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x73, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x69, 0x73, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x12, 0x3c, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0xd6, 0x01, 0x0a, 0x05, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x73, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x69, 0x73, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x3c, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x3e, 0x0a, 0x07, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x07, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x73, 0x1a, 0x18, 0x0a, 0x06, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x71, 0x0a, 0x07, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x12, 0x0a, 0x0e, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x57, 0x65, 0x73, 0x68, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x42, 0x4c, 0x45, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x10, 0x02, 0x12, 0x10, 0x0a, 0x0c, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x10, 0x03, 0x12, 0x0e, 0x0a, 0x0a, 0x54, 0x6f, 0x72, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x10, 0x04, 0x12, 0x0f, 0x0a, 0x0b, 0x51, 0x75, 0x69, 0x63, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x10, 0x05, 0x22, 0x9c, 0x01, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x64, 0x6f, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x64, 0x6f, 0x69, 0x6e, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x02, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x22, 0xc7, 0x01, 0x0a, 0x11, 0x4f, 0x75, 0x74, 0x4f, 0x66, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x63, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x63, 0x69, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6b, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x06, 0x52, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x69, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x73, 0x69, 0x67, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x07, 0x52, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x22, 0x6c, 0x0a, 0x19, 0x4f, 0x75, 0x74, 0x4f, 0x66, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x62, 0x6f, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x62, 0x6f, 0x78, 0x12, 0x27, 0x0a, 0x0f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x22, 0xf7, 0x01, 0x0a, 0x11, 0x4f, 0x75, 0x74, 0x4f, 0x66, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x1a, 0x23, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x1a, 0xbc, 0x01, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x40, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x4f, 0x66, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6c, 0x65, 0x61, 0x72, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6c, 0x65, 0x61, 0x72, 0x74, 0x65, 0x78, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x10, 0x61, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x61, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x22, 0x7e, 0x0a, 0x0e, 0x4f, 0x75, 0x74, 0x4f, 0x66, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x53, 0x65, 0x61, 0x6c, 0x1a, 0x45, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x63, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x63, 0x69, 0x64, 0x12, 0x28, 0x0a, 0x10, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x1a, 0x25, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x22, 0xbe, 0x02, 0x0a, 0x23, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6b, 0x12, 0x3b, 0x0a, 0x1a, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x17, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x2f, 0x0a, 0x13, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x2b, 0x0a, 0x11, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x73, 0x73, 0x75, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x69, 0x73, 0x73, 0x75, 0x65, 0x72, 0x22, 0x3d, 0x0a, 0x11, 0x46, 0x69, 0x72, 0x73, 0x74, 0x4c, 0x61, 0x73, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x69, 0x72, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x66, 0x69, 0x72, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x61, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x6c, 0x61, 0x73, 0x74, 0x22, 0xc4, 0x01, 0x0a, 0x13, 0x4f, 0x72, 0x62, 0x69, 0x74, 0x44, 0x42, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x65, 0x61, 0x64, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x61, 0x6c, 0x65, 0x64, 0x5f, 0x62, 0x6f, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x65, 0x61, 0x6c, 0x65, 0x64, 0x42, 0x6f, 0x78, 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x61, 0x77, 0x5f, 0x72, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x72, 0x61, 0x77, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x6b, 0x0a, 0x03, 0x42, 0x6f, 0x78, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x68, 0x65, 0x61, 0x64, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x68, 0x65, 0x61, 0x64, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6b, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70, 0x65, 0x65, 0x72, 0x49, 0x64, 0x22, 0xe4, 0x01, 0x0a, 0x15, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x0a, 0x04, 0x50, 0x65, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x64, 0x64, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x61, 0x64, 0x64, 0x72, 0x73, 0x1a, 0x42, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x50, 0x6b, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x1a, 0x59, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x50, 0x0a, 0x0b, 0x70, 0x65, 0x65, 0x72, 0x73, 0x5f, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x52, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x73, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x2a, 0x69, 0x0a, 0x09, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x12, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x54, 0x79, 0x70, 0x65, 0x55, 0x6e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x54, 0x79, 0x70, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x10, 0x01, 0x12, 0x14, 0x0a, 0x10, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x10, 0x02, 0x12, 0x18, 0x0a, 0x14, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x54, 0x79, 0x70, 0x65, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x10, 0x03, 0x2a, 0xba, 0x07, 0x0a, 0x09, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x12, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x55, 0x6e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x10, 0x00, 0x12, 0x23, 0x0a, 0x1f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x64, 0x64, 0x65, 0x64, 0x10, 0x01, 0x12, 0x25, 0x0a, 0x21, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x4b, 0x65, 0x79, 0x41, 0x64, 0x64, 0x65, 0x64, 0x10, 0x02, 0x12, 0x1f, 0x0a, 0x1b, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4a, 0x6f, 0x69, 0x6e, 0x65, 0x64, 0x10, 0x65, 0x12, 0x1d, 0x0a, 0x19, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4c, 0x65, 0x66, 0x74, 0x10, 0x66, 0x12, 0x2a, 0x0a, 0x26, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x10, 0x67, 0x12, 0x29, 0x0a, 0x25, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x10, 0x68, 0x12, 0x30, 0x0a, 0x2c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x65, 0x74, 0x10, 0x69, 0x12, 0x32, 0x0a, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x71, 0x75, 0x65, 0x75, 0x65, 0x64, 0x10, 0x6a, 0x12, 0x2e, 0x0a, 0x2a, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x6e, 0x74, 0x10, 0x6b, 0x12, 0x32, 0x0a, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x10, 0x6c, 0x12, 0x33, 0x0a, 0x2f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x44, 0x69, 0x73, 0x63, 0x61, 0x72, 0x64, 0x65, 0x64, 0x10, 0x6d, 0x12, 0x32, 0x0a, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x10, 0x6e, 0x12, 0x22, 0x0a, 0x1e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x10, 0x6f, 0x12, 0x24, 0x0a, 0x20, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x55, 0x6e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x10, 0x70, 0x12, 0x22, 0x0a, 0x1d, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x41, 0x64, 0x64, 0x65, 0x64, 0x10, 0xc9, 0x01, 0x12, 0x30, 0x0a, 0x2b, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x41, 0x64, 0x64, 0x65, 0x64, 0x10, 0xad, 0x02, 0x12, 0x34, 0x0a, 0x2f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x64, 0x10, 0xae, 0x02, 0x12, 0x2e, 0x0a, 0x29, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x65, 0x64, 0x10, 0xaf, 0x02, 0x12, 0x1e, 0x0a, 0x19, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x10, 0x93, 0x03, 0x12, 0x31, 0x0a, 0x2c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x10, 0xf4, 0x03, 0x12, 0x26, 0x0a, 0x21, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x65, 0x6e, 0x74, 0x10, 0xe9, 0x07, 0x2a, 0x8c, 0x01, 0x0a, 0x18, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4c, 0x6f, 0x67, 0x54, 0x79, 0x70, 0x65, 0x12, 0x25, 0x0a, 0x21, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4c, 0x6f, 0x67, 0x54, 0x79, 0x70, 0x65, 0x55, 0x6e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x10, 0x00, 0x12, 0x23, 0x0a, 0x1f, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4c, 0x6f, 0x67, 0x54, 0x79, 0x70, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x10, 0x01, 0x12, 0x24, 0x0a, 0x20, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4c, 0x6f, 0x67, 0x54, 0x79, 0x70, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x10, 0x02, 0x2a, 0xc2, 0x01, 0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x19, 0x0a, 0x15, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x55, 0x6e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x10, 0x01, 0x12, 0x18, 0x0a, 0x14, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x10, 0x02, 0x12, 0x15, 0x0a, 0x11, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x41, 0x64, 0x64, 0x65, 0x64, 0x10, 0x03, 0x12, 0x17, 0x0a, 0x13, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x10, 0x04, 0x12, 0x19, 0x0a, 0x15, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x44, 0x69, 0x73, 0x63, 0x61, 0x72, 0x64, 0x65, 0x64, 0x10, 0x05, 0x12, 0x17, 0x0a, 0x13, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x10, 0x06, 0x2a, 0x47, 0x0a, 0x09, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0e, 0x0a, 0x0a, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x44, 0x69, 0x72, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x44, 0x69, 0x72, 0x10, 0x01, 0x12, 0x0f, 0x0a, 0x0b, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x44, 0x69, 0x72, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x42, 0x69, 0x44, 0x69, 0x72, 0x10, 0x03, 0x32, 0xdd, 0x26, 0x0a, 0x0f, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x73, 0x0a, 0x11, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x12, 0x2e, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x30, 0x01, 0x12, 0x83, 0x01, 0x0a, 0x17, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x34, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x83, 0x01, 0x0a, 0x17, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x34, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x7d, 0x0a, 0x15, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x32, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x7a, 0x0a, 0x14, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x31, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x92, 0x01, 0x0a, 0x1c, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x65, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x39, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x65, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x65, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x74, 0x0a, 0x12, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x65, 0x6e, 0x64, 0x12, 0x2f, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x65, 0x6e, 0x64, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x65, 0x6e, 0x64, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x7a, 0x0a, 0x14, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x12, 0x31, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x7d, 0x0a, 0x15, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x44, 0x69, 0x73, 0x63, 0x61, 0x72, 0x64, 0x12, 0x32, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x44, 0x69, 0x73, 0x63, 0x61, 0x72, 0x64, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x44, 0x69, 0x73, 0x63, 0x61, 0x72, 0x64, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x62, 0x0a, 0x0c, 0x53, 0x68, 0x61, 0x72, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x12, 0x29, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x65, 0x0a, 0x0d, 0x44, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x12, 0x2a, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x62, 0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x29, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x68, 0x0a, 0x0e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x55, 0x6e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x2b, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x55, 0x6e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x55, 0x6e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x77, 0x0a, 0x13, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x53, 0x65, 0x6e, 0x64, 0x12, 0x30, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x53, 0x65, 0x6e, 0x64, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x53, 0x65, 0x6e, 0x64, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x80, 0x01, 0x0a, 0x16, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x33, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x7a, 0x0a, 0x14, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4a, 0x6f, 0x69, 0x6e, 0x12, 0x31, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4a, 0x6f, 0x69, 0x6e, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4a, 0x6f, 0x69, 0x6e, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x7d, 0x0a, 0x15, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x12, 0x32, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0xad, 0x01, 0x0a, 0x25, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x44, 0x69, 0x73, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x12, 0x42, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x44, 0x69, 0x73, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x40, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x44, 0x69, 0x73, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x98, 0x01, 0x0a, 0x1e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x12, 0x3b, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x39, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x9e, 0x01, 0x0a, 0x20, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x3d, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3b, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x6b, 0x0a, 0x0f, 0x41, 0x70, 0x70, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x53, 0x65, 0x6e, 0x64, 0x12, 0x2c, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x70, 0x70, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x53, 0x65, 0x6e, 0x64, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x70, 0x70, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x53, 0x65, 0x6e, 0x64, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x68, 0x0a, 0x0e, 0x41, 0x70, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x53, 0x65, 0x6e, 0x64, 0x12, 0x2b, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x70, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x53, 0x65, 0x6e, 0x64, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x70, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x53, 0x65, 0x6e, 0x64, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x6e, 0x0a, 0x11, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x2e, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x4c, 0x69, 0x73, 0x74, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x6b, 0x0a, 0x10, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x2d, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x59, 0x0a, 0x09, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x26, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x65, 0x0a, 0x0d, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x2a, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x6b, 0x0a, 0x0f, 0x44, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x2c, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x73, 0x0a, 0x11, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2e, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x30, 0x01, 0x12, 0x6d, 0x0a, 0x0f, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x69, 0x73, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x2c, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x69, 0x73, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x69, 0x73, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x30, 0x01, 0x12, 0x82, 0x01, 0x0a, 0x16, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x33, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x30, 0x01, 0x12, 0x5c, 0x0a, 0x0a, 0x44, 0x65, 0x62, 0x75, 0x67, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x27, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x5c, 0x0a, 0x0a, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x27, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0xad, 0x01, 0x0a, 0x25, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x69, 0x74, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x42, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x69, 0x74, 0x46, 0x6c, 0x6f, 0x77, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x40, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x69, 0x74, 0x46, 0x6c, 0x6f, 0x77, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0xb9, 0x01, 0x0a, 0x29, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x46, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x46, 0x6c, 0x6f, 0x77, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x44, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x46, 0x6c, 0x6f, 0x77, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x85, 0x01, 0x0a, 0x17, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x34, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x30, 0x01, 0x12, 0x9b, 0x01, 0x0a, 0x1f, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x3c, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3a, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x56, 0x0a, 0x08, 0x50, 0x65, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x25, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x71, 0x0a, 0x11, 0x4f, 0x75, 0x74, 0x4f, 0x66, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x12, 0x2e, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x4f, 0x66, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x4f, 0x66, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x68, 0x0a, 0x0e, 0x4f, 0x75, 0x74, 0x4f, 0x66, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x53, 0x65, 0x61, 0x6c, 0x12, 0x2b, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x4f, 0x66, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x53, 0x65, 0x61, 0x6c, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x4f, 0x66, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x53, 0x65, 0x61, 0x6c, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x7d, 0x0a, 0x15, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x32, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x42, 0x29, 0x5a, 0x27, 0x62, 0x65, 0x72, 0x74, 0x79, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x2f, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2f, 0x76, 0x32, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x74, 0x79, 0x70, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( file_protocoltypes_proto_rawDescOnce sync.Once file_protocoltypes_proto_rawDescData = file_protocoltypes_proto_rawDesc ) func file_protocoltypes_proto_rawDescGZIP() []byte { file_protocoltypes_proto_rawDescOnce.Do(func() { file_protocoltypes_proto_rawDescData = protoimpl.X.CompressGZIP(file_protocoltypes_proto_rawDescData) }) return file_protocoltypes_proto_rawDescData } var file_protocoltypes_proto_enumTypes = make([]protoimpl.EnumInfo, 9) var file_protocoltypes_proto_msgTypes = make([]protoimpl.MessageInfo, 178) var file_protocoltypes_proto_goTypes = []any{ (GroupType)(0), // 0: weshnet.protocol.v1.GroupType (EventType)(0), // 1: weshnet.protocol.v1.EventType (DebugInspectGroupLogType)(0), // 2: weshnet.protocol.v1.DebugInspectGroupLogType (ContactState)(0), // 3: weshnet.protocol.v1.ContactState (Direction)(0), // 4: weshnet.protocol.v1.Direction (ServiceGetConfiguration_SettingState)(0), // 5: weshnet.protocol.v1.ServiceGetConfiguration.SettingState (GroupDeviceStatus_Type)(0), // 6: weshnet.protocol.v1.GroupDeviceStatus.Type (GroupDeviceStatus_Transport)(0), // 7: weshnet.protocol.v1.GroupDeviceStatus.Transport (PeerList_Feature)(0), // 8: weshnet.protocol.v1.PeerList.Feature (*Account)(nil), // 9: weshnet.protocol.v1.Account (*Group)(nil), // 10: weshnet.protocol.v1.Group (*GroupHeadsExport)(nil), // 11: weshnet.protocol.v1.GroupHeadsExport (*GroupMetadata)(nil), // 12: weshnet.protocol.v1.GroupMetadata (*GroupEnvelope)(nil), // 13: weshnet.protocol.v1.GroupEnvelope (*MessageHeaders)(nil), // 14: weshnet.protocol.v1.MessageHeaders (*ProtocolMetadata)(nil), // 15: weshnet.protocol.v1.ProtocolMetadata (*EncryptedMessage)(nil), // 16: weshnet.protocol.v1.EncryptedMessage (*MessageEnvelope)(nil), // 17: weshnet.protocol.v1.MessageEnvelope (*EventContext)(nil), // 18: weshnet.protocol.v1.EventContext (*GroupMetadataPayloadSent)(nil), // 19: weshnet.protocol.v1.GroupMetadataPayloadSent (*ContactAliasKeyAdded)(nil), // 20: weshnet.protocol.v1.ContactAliasKeyAdded (*GroupMemberDeviceAdded)(nil), // 21: weshnet.protocol.v1.GroupMemberDeviceAdded (*DeviceChainKey)(nil), // 22: weshnet.protocol.v1.DeviceChainKey (*GroupDeviceChainKeyAdded)(nil), // 23: weshnet.protocol.v1.GroupDeviceChainKeyAdded (*MultiMemberGroupAliasResolverAdded)(nil), // 24: weshnet.protocol.v1.MultiMemberGroupAliasResolverAdded (*MultiMemberGroupAdminRoleGranted)(nil), // 25: weshnet.protocol.v1.MultiMemberGroupAdminRoleGranted (*MultiMemberGroupInitialMemberAnnounced)(nil), // 26: weshnet.protocol.v1.MultiMemberGroupInitialMemberAnnounced (*GroupAddAdditionalRendezvousSeed)(nil), // 27: weshnet.protocol.v1.GroupAddAdditionalRendezvousSeed (*GroupRemoveAdditionalRendezvousSeed)(nil), // 28: weshnet.protocol.v1.GroupRemoveAdditionalRendezvousSeed (*AccountGroupJoined)(nil), // 29: weshnet.protocol.v1.AccountGroupJoined (*AccountGroupLeft)(nil), // 30: weshnet.protocol.v1.AccountGroupLeft (*AccountContactRequestDisabled)(nil), // 31: weshnet.protocol.v1.AccountContactRequestDisabled (*AccountContactRequestEnabled)(nil), // 32: weshnet.protocol.v1.AccountContactRequestEnabled (*AccountContactRequestReferenceReset)(nil), // 33: weshnet.protocol.v1.AccountContactRequestReferenceReset (*AccountContactRequestOutgoingEnqueued)(nil), // 34: weshnet.protocol.v1.AccountContactRequestOutgoingEnqueued (*AccountContactRequestOutgoingSent)(nil), // 35: weshnet.protocol.v1.AccountContactRequestOutgoingSent (*AccountContactRequestIncomingReceived)(nil), // 36: weshnet.protocol.v1.AccountContactRequestIncomingReceived (*AccountContactRequestIncomingDiscarded)(nil), // 37: weshnet.protocol.v1.AccountContactRequestIncomingDiscarded (*AccountContactRequestIncomingAccepted)(nil), // 38: weshnet.protocol.v1.AccountContactRequestIncomingAccepted (*AccountContactBlocked)(nil), // 39: weshnet.protocol.v1.AccountContactBlocked (*AccountContactUnblocked)(nil), // 40: weshnet.protocol.v1.AccountContactUnblocked (*GroupReplicating)(nil), // 41: weshnet.protocol.v1.GroupReplicating (*ServiceExportData)(nil), // 42: weshnet.protocol.v1.ServiceExportData (*ServiceGetConfiguration)(nil), // 43: weshnet.protocol.v1.ServiceGetConfiguration (*ContactRequestReference)(nil), // 44: weshnet.protocol.v1.ContactRequestReference (*ContactRequestDisable)(nil), // 45: weshnet.protocol.v1.ContactRequestDisable (*ContactRequestEnable)(nil), // 46: weshnet.protocol.v1.ContactRequestEnable (*ContactRequestResetReference)(nil), // 47: weshnet.protocol.v1.ContactRequestResetReference (*ContactRequestSend)(nil), // 48: weshnet.protocol.v1.ContactRequestSend (*ContactRequestAccept)(nil), // 49: weshnet.protocol.v1.ContactRequestAccept (*ContactRequestDiscard)(nil), // 50: weshnet.protocol.v1.ContactRequestDiscard (*ShareContact)(nil), // 51: weshnet.protocol.v1.ShareContact (*DecodeContact)(nil), // 52: weshnet.protocol.v1.DecodeContact (*ContactBlock)(nil), // 53: weshnet.protocol.v1.ContactBlock (*ContactUnblock)(nil), // 54: weshnet.protocol.v1.ContactUnblock (*ContactAliasKeySend)(nil), // 55: weshnet.protocol.v1.ContactAliasKeySend (*MultiMemberGroupCreate)(nil), // 56: weshnet.protocol.v1.MultiMemberGroupCreate (*MultiMemberGroupJoin)(nil), // 57: weshnet.protocol.v1.MultiMemberGroupJoin (*MultiMemberGroupLeave)(nil), // 58: weshnet.protocol.v1.MultiMemberGroupLeave (*MultiMemberGroupAliasResolverDisclose)(nil), // 59: weshnet.protocol.v1.MultiMemberGroupAliasResolverDisclose (*MultiMemberGroupAdminRoleGrant)(nil), // 60: weshnet.protocol.v1.MultiMemberGroupAdminRoleGrant (*MultiMemberGroupInvitationCreate)(nil), // 61: weshnet.protocol.v1.MultiMemberGroupInvitationCreate (*AppMetadataSend)(nil), // 62: weshnet.protocol.v1.AppMetadataSend (*AppMessageSend)(nil), // 63: weshnet.protocol.v1.AppMessageSend (*GroupMetadataEvent)(nil), // 64: weshnet.protocol.v1.GroupMetadataEvent (*GroupMessageEvent)(nil), // 65: weshnet.protocol.v1.GroupMessageEvent (*GroupMetadataList)(nil), // 66: weshnet.protocol.v1.GroupMetadataList (*GroupMessageList)(nil), // 67: weshnet.protocol.v1.GroupMessageList (*GroupInfo)(nil), // 68: weshnet.protocol.v1.GroupInfo (*ActivateGroup)(nil), // 69: weshnet.protocol.v1.ActivateGroup (*DeactivateGroup)(nil), // 70: weshnet.protocol.v1.DeactivateGroup (*GroupDeviceStatus)(nil), // 71: weshnet.protocol.v1.GroupDeviceStatus (*DebugListGroups)(nil), // 72: weshnet.protocol.v1.DebugListGroups (*DebugInspectGroupStore)(nil), // 73: weshnet.protocol.v1.DebugInspectGroupStore (*DebugGroup)(nil), // 74: weshnet.protocol.v1.DebugGroup (*ShareableContact)(nil), // 75: weshnet.protocol.v1.ShareableContact (*ServiceTokenSupportedService)(nil), // 76: weshnet.protocol.v1.ServiceTokenSupportedService (*ServiceToken)(nil), // 77: weshnet.protocol.v1.ServiceToken (*CredentialVerificationServiceInitFlow)(nil), // 78: weshnet.protocol.v1.CredentialVerificationServiceInitFlow (*CredentialVerificationServiceCompleteFlow)(nil), // 79: weshnet.protocol.v1.CredentialVerificationServiceCompleteFlow (*VerifiedCredentialsList)(nil), // 80: weshnet.protocol.v1.VerifiedCredentialsList (*ReplicationServiceRegisterGroup)(nil), // 81: weshnet.protocol.v1.ReplicationServiceRegisterGroup (*ReplicationServiceReplicateGroup)(nil), // 82: weshnet.protocol.v1.ReplicationServiceReplicateGroup (*SystemInfo)(nil), // 83: weshnet.protocol.v1.SystemInfo (*PeerList)(nil), // 84: weshnet.protocol.v1.PeerList (*Progress)(nil), // 85: weshnet.protocol.v1.Progress (*OutOfStoreMessage)(nil), // 86: weshnet.protocol.v1.OutOfStoreMessage (*OutOfStoreMessageEnvelope)(nil), // 87: weshnet.protocol.v1.OutOfStoreMessageEnvelope (*OutOfStoreReceive)(nil), // 88: weshnet.protocol.v1.OutOfStoreReceive (*OutOfStoreSeal)(nil), // 89: weshnet.protocol.v1.OutOfStoreSeal (*AccountVerifiedCredentialRegistered)(nil), // 90: weshnet.protocol.v1.AccountVerifiedCredentialRegistered (*FirstLastCounters)(nil), // 91: weshnet.protocol.v1.FirstLastCounters (*OrbitDBMessageHeads)(nil), // 92: weshnet.protocol.v1.OrbitDBMessageHeads (*RefreshContactRequest)(nil), // 93: weshnet.protocol.v1.RefreshContactRequest nil, // 94: weshnet.protocol.v1.MessageHeaders.MetadataEntry (*ServiceExportData_Request)(nil), // 95: weshnet.protocol.v1.ServiceExportData.Request (*ServiceExportData_Reply)(nil), // 96: weshnet.protocol.v1.ServiceExportData.Reply (*ServiceGetConfiguration_Request)(nil), // 97: weshnet.protocol.v1.ServiceGetConfiguration.Request (*ServiceGetConfiguration_Reply)(nil), // 98: weshnet.protocol.v1.ServiceGetConfiguration.Reply (*ContactRequestReference_Request)(nil), // 99: weshnet.protocol.v1.ContactRequestReference.Request (*ContactRequestReference_Reply)(nil), // 100: weshnet.protocol.v1.ContactRequestReference.Reply (*ContactRequestDisable_Request)(nil), // 101: weshnet.protocol.v1.ContactRequestDisable.Request (*ContactRequestDisable_Reply)(nil), // 102: weshnet.protocol.v1.ContactRequestDisable.Reply (*ContactRequestEnable_Request)(nil), // 103: weshnet.protocol.v1.ContactRequestEnable.Request (*ContactRequestEnable_Reply)(nil), // 104: weshnet.protocol.v1.ContactRequestEnable.Reply (*ContactRequestResetReference_Request)(nil), // 105: weshnet.protocol.v1.ContactRequestResetReference.Request (*ContactRequestResetReference_Reply)(nil), // 106: weshnet.protocol.v1.ContactRequestResetReference.Reply (*ContactRequestSend_Request)(nil), // 107: weshnet.protocol.v1.ContactRequestSend.Request (*ContactRequestSend_Reply)(nil), // 108: weshnet.protocol.v1.ContactRequestSend.Reply (*ContactRequestAccept_Request)(nil), // 109: weshnet.protocol.v1.ContactRequestAccept.Request (*ContactRequestAccept_Reply)(nil), // 110: weshnet.protocol.v1.ContactRequestAccept.Reply (*ContactRequestDiscard_Request)(nil), // 111: weshnet.protocol.v1.ContactRequestDiscard.Request (*ContactRequestDiscard_Reply)(nil), // 112: weshnet.protocol.v1.ContactRequestDiscard.Reply (*ShareContact_Request)(nil), // 113: weshnet.protocol.v1.ShareContact.Request (*ShareContact_Reply)(nil), // 114: weshnet.protocol.v1.ShareContact.Reply (*DecodeContact_Request)(nil), // 115: weshnet.protocol.v1.DecodeContact.Request (*DecodeContact_Reply)(nil), // 116: weshnet.protocol.v1.DecodeContact.Reply (*ContactBlock_Request)(nil), // 117: weshnet.protocol.v1.ContactBlock.Request (*ContactBlock_Reply)(nil), // 118: weshnet.protocol.v1.ContactBlock.Reply (*ContactUnblock_Request)(nil), // 119: weshnet.protocol.v1.ContactUnblock.Request (*ContactUnblock_Reply)(nil), // 120: weshnet.protocol.v1.ContactUnblock.Reply (*ContactAliasKeySend_Request)(nil), // 121: weshnet.protocol.v1.ContactAliasKeySend.Request (*ContactAliasKeySend_Reply)(nil), // 122: weshnet.protocol.v1.ContactAliasKeySend.Reply (*MultiMemberGroupCreate_Request)(nil), // 123: weshnet.protocol.v1.MultiMemberGroupCreate.Request (*MultiMemberGroupCreate_Reply)(nil), // 124: weshnet.protocol.v1.MultiMemberGroupCreate.Reply (*MultiMemberGroupJoin_Request)(nil), // 125: weshnet.protocol.v1.MultiMemberGroupJoin.Request (*MultiMemberGroupJoin_Reply)(nil), // 126: weshnet.protocol.v1.MultiMemberGroupJoin.Reply (*MultiMemberGroupLeave_Request)(nil), // 127: weshnet.protocol.v1.MultiMemberGroupLeave.Request (*MultiMemberGroupLeave_Reply)(nil), // 128: weshnet.protocol.v1.MultiMemberGroupLeave.Reply (*MultiMemberGroupAliasResolverDisclose_Request)(nil), // 129: weshnet.protocol.v1.MultiMemberGroupAliasResolverDisclose.Request (*MultiMemberGroupAliasResolverDisclose_Reply)(nil), // 130: weshnet.protocol.v1.MultiMemberGroupAliasResolverDisclose.Reply (*MultiMemberGroupAdminRoleGrant_Request)(nil), // 131: weshnet.protocol.v1.MultiMemberGroupAdminRoleGrant.Request (*MultiMemberGroupAdminRoleGrant_Reply)(nil), // 132: weshnet.protocol.v1.MultiMemberGroupAdminRoleGrant.Reply (*MultiMemberGroupInvitationCreate_Request)(nil), // 133: weshnet.protocol.v1.MultiMemberGroupInvitationCreate.Request (*MultiMemberGroupInvitationCreate_Reply)(nil), // 134: weshnet.protocol.v1.MultiMemberGroupInvitationCreate.Reply (*AppMetadataSend_Request)(nil), // 135: weshnet.protocol.v1.AppMetadataSend.Request (*AppMetadataSend_Reply)(nil), // 136: weshnet.protocol.v1.AppMetadataSend.Reply (*AppMessageSend_Request)(nil), // 137: weshnet.protocol.v1.AppMessageSend.Request (*AppMessageSend_Reply)(nil), // 138: weshnet.protocol.v1.AppMessageSend.Reply (*GroupMetadataList_Request)(nil), // 139: weshnet.protocol.v1.GroupMetadataList.Request (*GroupMessageList_Request)(nil), // 140: weshnet.protocol.v1.GroupMessageList.Request (*GroupInfo_Request)(nil), // 141: weshnet.protocol.v1.GroupInfo.Request (*GroupInfo_Reply)(nil), // 142: weshnet.protocol.v1.GroupInfo.Reply (*ActivateGroup_Request)(nil), // 143: weshnet.protocol.v1.ActivateGroup.Request (*ActivateGroup_Reply)(nil), // 144: weshnet.protocol.v1.ActivateGroup.Reply (*DeactivateGroup_Request)(nil), // 145: weshnet.protocol.v1.DeactivateGroup.Request (*DeactivateGroup_Reply)(nil), // 146: weshnet.protocol.v1.DeactivateGroup.Reply (*GroupDeviceStatus_Request)(nil), // 147: weshnet.protocol.v1.GroupDeviceStatus.Request (*GroupDeviceStatus_Reply)(nil), // 148: weshnet.protocol.v1.GroupDeviceStatus.Reply (*GroupDeviceStatus_Reply_PeerConnected)(nil), // 149: weshnet.protocol.v1.GroupDeviceStatus.Reply.PeerConnected (*GroupDeviceStatus_Reply_PeerReconnecting)(nil), // 150: weshnet.protocol.v1.GroupDeviceStatus.Reply.PeerReconnecting (*GroupDeviceStatus_Reply_PeerDisconnected)(nil), // 151: weshnet.protocol.v1.GroupDeviceStatus.Reply.PeerDisconnected (*DebugListGroups_Request)(nil), // 152: weshnet.protocol.v1.DebugListGroups.Request (*DebugListGroups_Reply)(nil), // 153: weshnet.protocol.v1.DebugListGroups.Reply (*DebugInspectGroupStore_Request)(nil), // 154: weshnet.protocol.v1.DebugInspectGroupStore.Request (*DebugInspectGroupStore_Reply)(nil), // 155: weshnet.protocol.v1.DebugInspectGroupStore.Reply (*DebugGroup_Request)(nil), // 156: weshnet.protocol.v1.DebugGroup.Request (*DebugGroup_Reply)(nil), // 157: weshnet.protocol.v1.DebugGroup.Reply (*CredentialVerificationServiceInitFlow_Request)(nil), // 158: weshnet.protocol.v1.CredentialVerificationServiceInitFlow.Request (*CredentialVerificationServiceInitFlow_Reply)(nil), // 159: weshnet.protocol.v1.CredentialVerificationServiceInitFlow.Reply (*CredentialVerificationServiceCompleteFlow_Request)(nil), // 160: weshnet.protocol.v1.CredentialVerificationServiceCompleteFlow.Request (*CredentialVerificationServiceCompleteFlow_Reply)(nil), // 161: weshnet.protocol.v1.CredentialVerificationServiceCompleteFlow.Reply (*VerifiedCredentialsList_Request)(nil), // 162: weshnet.protocol.v1.VerifiedCredentialsList.Request (*VerifiedCredentialsList_Reply)(nil), // 163: weshnet.protocol.v1.VerifiedCredentialsList.Reply (*ReplicationServiceRegisterGroup_Request)(nil), // 164: weshnet.protocol.v1.ReplicationServiceRegisterGroup.Request (*ReplicationServiceRegisterGroup_Reply)(nil), // 165: weshnet.protocol.v1.ReplicationServiceRegisterGroup.Reply (*ReplicationServiceReplicateGroup_Request)(nil), // 166: weshnet.protocol.v1.ReplicationServiceReplicateGroup.Request (*ReplicationServiceReplicateGroup_Reply)(nil), // 167: weshnet.protocol.v1.ReplicationServiceReplicateGroup.Reply (*SystemInfo_Request)(nil), // 168: weshnet.protocol.v1.SystemInfo.Request (*SystemInfo_Reply)(nil), // 169: weshnet.protocol.v1.SystemInfo.Reply (*SystemInfo_OrbitDB)(nil), // 170: weshnet.protocol.v1.SystemInfo.OrbitDB (*SystemInfo_P2P)(nil), // 171: weshnet.protocol.v1.SystemInfo.P2P (*SystemInfo_Process)(nil), // 172: weshnet.protocol.v1.SystemInfo.Process (*SystemInfo_OrbitDB_ReplicationStatus)(nil), // 173: weshnet.protocol.v1.SystemInfo.OrbitDB.ReplicationStatus (*PeerList_Request)(nil), // 174: weshnet.protocol.v1.PeerList.Request (*PeerList_Reply)(nil), // 175: weshnet.protocol.v1.PeerList.Reply (*PeerList_Peer)(nil), // 176: weshnet.protocol.v1.PeerList.Peer (*PeerList_Route)(nil), // 177: weshnet.protocol.v1.PeerList.Route (*PeerList_Stream)(nil), // 178: weshnet.protocol.v1.PeerList.Stream (*OutOfStoreReceive_Request)(nil), // 179: weshnet.protocol.v1.OutOfStoreReceive.Request (*OutOfStoreReceive_Reply)(nil), // 180: weshnet.protocol.v1.OutOfStoreReceive.Reply (*OutOfStoreSeal_Request)(nil), // 181: weshnet.protocol.v1.OutOfStoreSeal.Request (*OutOfStoreSeal_Reply)(nil), // 182: weshnet.protocol.v1.OutOfStoreSeal.Reply (*OrbitDBMessageHeads_Box)(nil), // 183: weshnet.protocol.v1.OrbitDBMessageHeads.Box (*RefreshContactRequest_Peer)(nil), // 184: weshnet.protocol.v1.RefreshContactRequest.Peer (*RefreshContactRequest_Request)(nil), // 185: weshnet.protocol.v1.RefreshContactRequest.Request (*RefreshContactRequest_Reply)(nil), // 186: weshnet.protocol.v1.RefreshContactRequest.Reply } var file_protocoltypes_proto_depIdxs = []int32{ 10, // 0: weshnet.protocol.v1.Account.group:type_name -> weshnet.protocol.v1.Group 0, // 1: weshnet.protocol.v1.Group.group_type:type_name -> weshnet.protocol.v1.GroupType 1, // 2: weshnet.protocol.v1.GroupMetadata.event_type:type_name -> weshnet.protocol.v1.EventType 15, // 3: weshnet.protocol.v1.GroupMetadata.protocol_metadata:type_name -> weshnet.protocol.v1.ProtocolMetadata 94, // 4: weshnet.protocol.v1.MessageHeaders.metadata:type_name -> weshnet.protocol.v1.MessageHeaders.MetadataEntry 15, // 5: weshnet.protocol.v1.EncryptedMessage.protocol_metadata:type_name -> weshnet.protocol.v1.ProtocolMetadata 10, // 6: weshnet.protocol.v1.AccountGroupJoined.group:type_name -> weshnet.protocol.v1.Group 75, // 7: weshnet.protocol.v1.AccountContactRequestOutgoingEnqueued.contact:type_name -> weshnet.protocol.v1.ShareableContact 18, // 8: weshnet.protocol.v1.GroupMetadataEvent.event_context:type_name -> weshnet.protocol.v1.EventContext 12, // 9: weshnet.protocol.v1.GroupMetadataEvent.metadata:type_name -> weshnet.protocol.v1.GroupMetadata 18, // 10: weshnet.protocol.v1.GroupMessageEvent.event_context:type_name -> weshnet.protocol.v1.EventContext 14, // 11: weshnet.protocol.v1.GroupMessageEvent.headers:type_name -> weshnet.protocol.v1.MessageHeaders 76, // 12: weshnet.protocol.v1.ServiceToken.supported_services:type_name -> weshnet.protocol.v1.ServiceTokenSupportedService 5, // 13: weshnet.protocol.v1.ServiceGetConfiguration.Reply.ble_enabled:type_name -> weshnet.protocol.v1.ServiceGetConfiguration.SettingState 5, // 14: weshnet.protocol.v1.ServiceGetConfiguration.Reply.wifi_p2p_enabled:type_name -> weshnet.protocol.v1.ServiceGetConfiguration.SettingState 5, // 15: weshnet.protocol.v1.ServiceGetConfiguration.Reply.mdns_enabled:type_name -> weshnet.protocol.v1.ServiceGetConfiguration.SettingState 5, // 16: weshnet.protocol.v1.ServiceGetConfiguration.Reply.relay_enabled:type_name -> weshnet.protocol.v1.ServiceGetConfiguration.SettingState 75, // 17: weshnet.protocol.v1.ContactRequestSend.Request.contact:type_name -> weshnet.protocol.v1.ShareableContact 75, // 18: weshnet.protocol.v1.DecodeContact.Reply.contact:type_name -> weshnet.protocol.v1.ShareableContact 10, // 19: weshnet.protocol.v1.MultiMemberGroupJoin.Request.group:type_name -> weshnet.protocol.v1.Group 10, // 20: weshnet.protocol.v1.MultiMemberGroupInvitationCreate.Reply.group:type_name -> weshnet.protocol.v1.Group 10, // 21: weshnet.protocol.v1.GroupInfo.Reply.group:type_name -> weshnet.protocol.v1.Group 6, // 22: weshnet.protocol.v1.GroupDeviceStatus.Reply.type:type_name -> weshnet.protocol.v1.GroupDeviceStatus.Type 7, // 23: weshnet.protocol.v1.GroupDeviceStatus.Reply.PeerConnected.transports:type_name -> weshnet.protocol.v1.GroupDeviceStatus.Transport 0, // 24: weshnet.protocol.v1.DebugListGroups.Reply.group_type:type_name -> weshnet.protocol.v1.GroupType 2, // 25: weshnet.protocol.v1.DebugInspectGroupStore.Request.log_type:type_name -> weshnet.protocol.v1.DebugInspectGroupLogType 1, // 26: weshnet.protocol.v1.DebugInspectGroupStore.Reply.metadata_event_type:type_name -> weshnet.protocol.v1.EventType 90, // 27: weshnet.protocol.v1.VerifiedCredentialsList.Reply.credential:type_name -> weshnet.protocol.v1.AccountVerifiedCredentialRegistered 10, // 28: weshnet.protocol.v1.ReplicationServiceReplicateGroup.Request.group:type_name -> weshnet.protocol.v1.Group 172, // 29: weshnet.protocol.v1.SystemInfo.Reply.process:type_name -> weshnet.protocol.v1.SystemInfo.Process 171, // 30: weshnet.protocol.v1.SystemInfo.Reply.p2p:type_name -> weshnet.protocol.v1.SystemInfo.P2P 170, // 31: weshnet.protocol.v1.SystemInfo.Reply.orbitdb:type_name -> weshnet.protocol.v1.SystemInfo.OrbitDB 173, // 32: weshnet.protocol.v1.SystemInfo.OrbitDB.account_metadata:type_name -> weshnet.protocol.v1.SystemInfo.OrbitDB.ReplicationStatus 176, // 33: weshnet.protocol.v1.PeerList.Reply.peers:type_name -> weshnet.protocol.v1.PeerList.Peer 177, // 34: weshnet.protocol.v1.PeerList.Peer.routes:type_name -> weshnet.protocol.v1.PeerList.Route 8, // 35: weshnet.protocol.v1.PeerList.Peer.features:type_name -> weshnet.protocol.v1.PeerList.Feature 4, // 36: weshnet.protocol.v1.PeerList.Peer.direction:type_name -> weshnet.protocol.v1.Direction 4, // 37: weshnet.protocol.v1.PeerList.Route.direction:type_name -> weshnet.protocol.v1.Direction 178, // 38: weshnet.protocol.v1.PeerList.Route.streams:type_name -> weshnet.protocol.v1.PeerList.Stream 86, // 39: weshnet.protocol.v1.OutOfStoreReceive.Reply.message:type_name -> weshnet.protocol.v1.OutOfStoreMessage 184, // 40: weshnet.protocol.v1.RefreshContactRequest.Reply.peers_found:type_name -> weshnet.protocol.v1.RefreshContactRequest.Peer 95, // 41: weshnet.protocol.v1.ProtocolService.ServiceExportData:input_type -> weshnet.protocol.v1.ServiceExportData.Request 97, // 42: weshnet.protocol.v1.ProtocolService.ServiceGetConfiguration:input_type -> weshnet.protocol.v1.ServiceGetConfiguration.Request 99, // 43: weshnet.protocol.v1.ProtocolService.ContactRequestReference:input_type -> weshnet.protocol.v1.ContactRequestReference.Request 101, // 44: weshnet.protocol.v1.ProtocolService.ContactRequestDisable:input_type -> weshnet.protocol.v1.ContactRequestDisable.Request 103, // 45: weshnet.protocol.v1.ProtocolService.ContactRequestEnable:input_type -> weshnet.protocol.v1.ContactRequestEnable.Request 105, // 46: weshnet.protocol.v1.ProtocolService.ContactRequestResetReference:input_type -> weshnet.protocol.v1.ContactRequestResetReference.Request 107, // 47: weshnet.protocol.v1.ProtocolService.ContactRequestSend:input_type -> weshnet.protocol.v1.ContactRequestSend.Request 109, // 48: weshnet.protocol.v1.ProtocolService.ContactRequestAccept:input_type -> weshnet.protocol.v1.ContactRequestAccept.Request 111, // 49: weshnet.protocol.v1.ProtocolService.ContactRequestDiscard:input_type -> weshnet.protocol.v1.ContactRequestDiscard.Request 113, // 50: weshnet.protocol.v1.ProtocolService.ShareContact:input_type -> weshnet.protocol.v1.ShareContact.Request 115, // 51: weshnet.protocol.v1.ProtocolService.DecodeContact:input_type -> weshnet.protocol.v1.DecodeContact.Request 117, // 52: weshnet.protocol.v1.ProtocolService.ContactBlock:input_type -> weshnet.protocol.v1.ContactBlock.Request 119, // 53: weshnet.protocol.v1.ProtocolService.ContactUnblock:input_type -> weshnet.protocol.v1.ContactUnblock.Request 121, // 54: weshnet.protocol.v1.ProtocolService.ContactAliasKeySend:input_type -> weshnet.protocol.v1.ContactAliasKeySend.Request 123, // 55: weshnet.protocol.v1.ProtocolService.MultiMemberGroupCreate:input_type -> weshnet.protocol.v1.MultiMemberGroupCreate.Request 125, // 56: weshnet.protocol.v1.ProtocolService.MultiMemberGroupJoin:input_type -> weshnet.protocol.v1.MultiMemberGroupJoin.Request 127, // 57: weshnet.protocol.v1.ProtocolService.MultiMemberGroupLeave:input_type -> weshnet.protocol.v1.MultiMemberGroupLeave.Request 129, // 58: weshnet.protocol.v1.ProtocolService.MultiMemberGroupAliasResolverDisclose:input_type -> weshnet.protocol.v1.MultiMemberGroupAliasResolverDisclose.Request 131, // 59: weshnet.protocol.v1.ProtocolService.MultiMemberGroupAdminRoleGrant:input_type -> weshnet.protocol.v1.MultiMemberGroupAdminRoleGrant.Request 133, // 60: weshnet.protocol.v1.ProtocolService.MultiMemberGroupInvitationCreate:input_type -> weshnet.protocol.v1.MultiMemberGroupInvitationCreate.Request 135, // 61: weshnet.protocol.v1.ProtocolService.AppMetadataSend:input_type -> weshnet.protocol.v1.AppMetadataSend.Request 137, // 62: weshnet.protocol.v1.ProtocolService.AppMessageSend:input_type -> weshnet.protocol.v1.AppMessageSend.Request 139, // 63: weshnet.protocol.v1.ProtocolService.GroupMetadataList:input_type -> weshnet.protocol.v1.GroupMetadataList.Request 140, // 64: weshnet.protocol.v1.ProtocolService.GroupMessageList:input_type -> weshnet.protocol.v1.GroupMessageList.Request 141, // 65: weshnet.protocol.v1.ProtocolService.GroupInfo:input_type -> weshnet.protocol.v1.GroupInfo.Request 143, // 66: weshnet.protocol.v1.ProtocolService.ActivateGroup:input_type -> weshnet.protocol.v1.ActivateGroup.Request 145, // 67: weshnet.protocol.v1.ProtocolService.DeactivateGroup:input_type -> weshnet.protocol.v1.DeactivateGroup.Request 147, // 68: weshnet.protocol.v1.ProtocolService.GroupDeviceStatus:input_type -> weshnet.protocol.v1.GroupDeviceStatus.Request 152, // 69: weshnet.protocol.v1.ProtocolService.DebugListGroups:input_type -> weshnet.protocol.v1.DebugListGroups.Request 154, // 70: weshnet.protocol.v1.ProtocolService.DebugInspectGroupStore:input_type -> weshnet.protocol.v1.DebugInspectGroupStore.Request 156, // 71: weshnet.protocol.v1.ProtocolService.DebugGroup:input_type -> weshnet.protocol.v1.DebugGroup.Request 168, // 72: weshnet.protocol.v1.ProtocolService.SystemInfo:input_type -> weshnet.protocol.v1.SystemInfo.Request 158, // 73: weshnet.protocol.v1.ProtocolService.CredentialVerificationServiceInitFlow:input_type -> weshnet.protocol.v1.CredentialVerificationServiceInitFlow.Request 160, // 74: weshnet.protocol.v1.ProtocolService.CredentialVerificationServiceCompleteFlow:input_type -> weshnet.protocol.v1.CredentialVerificationServiceCompleteFlow.Request 162, // 75: weshnet.protocol.v1.ProtocolService.VerifiedCredentialsList:input_type -> weshnet.protocol.v1.VerifiedCredentialsList.Request 164, // 76: weshnet.protocol.v1.ProtocolService.ReplicationServiceRegisterGroup:input_type -> weshnet.protocol.v1.ReplicationServiceRegisterGroup.Request 174, // 77: weshnet.protocol.v1.ProtocolService.PeerList:input_type -> weshnet.protocol.v1.PeerList.Request 179, // 78: weshnet.protocol.v1.ProtocolService.OutOfStoreReceive:input_type -> weshnet.protocol.v1.OutOfStoreReceive.Request 181, // 79: weshnet.protocol.v1.ProtocolService.OutOfStoreSeal:input_type -> weshnet.protocol.v1.OutOfStoreSeal.Request 185, // 80: weshnet.protocol.v1.ProtocolService.RefreshContactRequest:input_type -> weshnet.protocol.v1.RefreshContactRequest.Request 96, // 81: weshnet.protocol.v1.ProtocolService.ServiceExportData:output_type -> weshnet.protocol.v1.ServiceExportData.Reply 98, // 82: weshnet.protocol.v1.ProtocolService.ServiceGetConfiguration:output_type -> weshnet.protocol.v1.ServiceGetConfiguration.Reply 100, // 83: weshnet.protocol.v1.ProtocolService.ContactRequestReference:output_type -> weshnet.protocol.v1.ContactRequestReference.Reply 102, // 84: weshnet.protocol.v1.ProtocolService.ContactRequestDisable:output_type -> weshnet.protocol.v1.ContactRequestDisable.Reply 104, // 85: weshnet.protocol.v1.ProtocolService.ContactRequestEnable:output_type -> weshnet.protocol.v1.ContactRequestEnable.Reply 106, // 86: weshnet.protocol.v1.ProtocolService.ContactRequestResetReference:output_type -> weshnet.protocol.v1.ContactRequestResetReference.Reply 108, // 87: weshnet.protocol.v1.ProtocolService.ContactRequestSend:output_type -> weshnet.protocol.v1.ContactRequestSend.Reply 110, // 88: weshnet.protocol.v1.ProtocolService.ContactRequestAccept:output_type -> weshnet.protocol.v1.ContactRequestAccept.Reply 112, // 89: weshnet.protocol.v1.ProtocolService.ContactRequestDiscard:output_type -> weshnet.protocol.v1.ContactRequestDiscard.Reply 114, // 90: weshnet.protocol.v1.ProtocolService.ShareContact:output_type -> weshnet.protocol.v1.ShareContact.Reply 116, // 91: weshnet.protocol.v1.ProtocolService.DecodeContact:output_type -> weshnet.protocol.v1.DecodeContact.Reply 118, // 92: weshnet.protocol.v1.ProtocolService.ContactBlock:output_type -> weshnet.protocol.v1.ContactBlock.Reply 120, // 93: weshnet.protocol.v1.ProtocolService.ContactUnblock:output_type -> weshnet.protocol.v1.ContactUnblock.Reply 122, // 94: weshnet.protocol.v1.ProtocolService.ContactAliasKeySend:output_type -> weshnet.protocol.v1.ContactAliasKeySend.Reply 124, // 95: weshnet.protocol.v1.ProtocolService.MultiMemberGroupCreate:output_type -> weshnet.protocol.v1.MultiMemberGroupCreate.Reply 126, // 96: weshnet.protocol.v1.ProtocolService.MultiMemberGroupJoin:output_type -> weshnet.protocol.v1.MultiMemberGroupJoin.Reply 128, // 97: weshnet.protocol.v1.ProtocolService.MultiMemberGroupLeave:output_type -> weshnet.protocol.v1.MultiMemberGroupLeave.Reply 130, // 98: weshnet.protocol.v1.ProtocolService.MultiMemberGroupAliasResolverDisclose:output_type -> weshnet.protocol.v1.MultiMemberGroupAliasResolverDisclose.Reply 132, // 99: weshnet.protocol.v1.ProtocolService.MultiMemberGroupAdminRoleGrant:output_type -> weshnet.protocol.v1.MultiMemberGroupAdminRoleGrant.Reply 134, // 100: weshnet.protocol.v1.ProtocolService.MultiMemberGroupInvitationCreate:output_type -> weshnet.protocol.v1.MultiMemberGroupInvitationCreate.Reply 136, // 101: weshnet.protocol.v1.ProtocolService.AppMetadataSend:output_type -> weshnet.protocol.v1.AppMetadataSend.Reply 138, // 102: weshnet.protocol.v1.ProtocolService.AppMessageSend:output_type -> weshnet.protocol.v1.AppMessageSend.Reply 64, // 103: weshnet.protocol.v1.ProtocolService.GroupMetadataList:output_type -> weshnet.protocol.v1.GroupMetadataEvent 65, // 104: weshnet.protocol.v1.ProtocolService.GroupMessageList:output_type -> weshnet.protocol.v1.GroupMessageEvent 142, // 105: weshnet.protocol.v1.ProtocolService.GroupInfo:output_type -> weshnet.protocol.v1.GroupInfo.Reply 144, // 106: weshnet.protocol.v1.ProtocolService.ActivateGroup:output_type -> weshnet.protocol.v1.ActivateGroup.Reply 146, // 107: weshnet.protocol.v1.ProtocolService.DeactivateGroup:output_type -> weshnet.protocol.v1.DeactivateGroup.Reply 148, // 108: weshnet.protocol.v1.ProtocolService.GroupDeviceStatus:output_type -> weshnet.protocol.v1.GroupDeviceStatus.Reply 153, // 109: weshnet.protocol.v1.ProtocolService.DebugListGroups:output_type -> weshnet.protocol.v1.DebugListGroups.Reply 155, // 110: weshnet.protocol.v1.ProtocolService.DebugInspectGroupStore:output_type -> weshnet.protocol.v1.DebugInspectGroupStore.Reply 157, // 111: weshnet.protocol.v1.ProtocolService.DebugGroup:output_type -> weshnet.protocol.v1.DebugGroup.Reply 169, // 112: weshnet.protocol.v1.ProtocolService.SystemInfo:output_type -> weshnet.protocol.v1.SystemInfo.Reply 159, // 113: weshnet.protocol.v1.ProtocolService.CredentialVerificationServiceInitFlow:output_type -> weshnet.protocol.v1.CredentialVerificationServiceInitFlow.Reply 161, // 114: weshnet.protocol.v1.ProtocolService.CredentialVerificationServiceCompleteFlow:output_type -> weshnet.protocol.v1.CredentialVerificationServiceCompleteFlow.Reply 163, // 115: weshnet.protocol.v1.ProtocolService.VerifiedCredentialsList:output_type -> weshnet.protocol.v1.VerifiedCredentialsList.Reply 165, // 116: weshnet.protocol.v1.ProtocolService.ReplicationServiceRegisterGroup:output_type -> weshnet.protocol.v1.ReplicationServiceRegisterGroup.Reply 175, // 117: weshnet.protocol.v1.ProtocolService.PeerList:output_type -> weshnet.protocol.v1.PeerList.Reply 180, // 118: weshnet.protocol.v1.ProtocolService.OutOfStoreReceive:output_type -> weshnet.protocol.v1.OutOfStoreReceive.Reply 182, // 119: weshnet.protocol.v1.ProtocolService.OutOfStoreSeal:output_type -> weshnet.protocol.v1.OutOfStoreSeal.Reply 186, // 120: weshnet.protocol.v1.ProtocolService.RefreshContactRequest:output_type -> weshnet.protocol.v1.RefreshContactRequest.Reply 81, // [81:121] is the sub-list for method output_type 41, // [41:81] is the sub-list for method input_type 41, // [41:41] is the sub-list for extension type_name 41, // [41:41] is the sub-list for extension extendee 0, // [0:41] is the sub-list for field type_name } func init() { file_protocoltypes_proto_init() } func file_protocoltypes_proto_init() { if File_protocoltypes_proto != nil { return } if !protoimpl.UnsafeEnabled { file_protocoltypes_proto_msgTypes[0].Exporter = func(v any, i int) any { switch v := v.(*Account); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[1].Exporter = func(v any, i int) any { switch v := v.(*Group); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[2].Exporter = func(v any, i int) any { switch v := v.(*GroupHeadsExport); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[3].Exporter = func(v any, i int) any { switch v := v.(*GroupMetadata); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[4].Exporter = func(v any, i int) any { switch v := v.(*GroupEnvelope); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[5].Exporter = func(v any, i int) any { switch v := v.(*MessageHeaders); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[6].Exporter = func(v any, i int) any { switch v := v.(*ProtocolMetadata); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[7].Exporter = func(v any, i int) any { switch v := v.(*EncryptedMessage); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[8].Exporter = func(v any, i int) any { switch v := v.(*MessageEnvelope); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[9].Exporter = func(v any, i int) any { switch v := v.(*EventContext); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[10].Exporter = func(v any, i int) any { switch v := v.(*GroupMetadataPayloadSent); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[11].Exporter = func(v any, i int) any { switch v := v.(*ContactAliasKeyAdded); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[12].Exporter = func(v any, i int) any { switch v := v.(*GroupMemberDeviceAdded); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[13].Exporter = func(v any, i int) any { switch v := v.(*DeviceChainKey); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[14].Exporter = func(v any, i int) any { switch v := v.(*GroupDeviceChainKeyAdded); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[15].Exporter = func(v any, i int) any { switch v := v.(*MultiMemberGroupAliasResolverAdded); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[16].Exporter = func(v any, i int) any { switch v := v.(*MultiMemberGroupAdminRoleGranted); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[17].Exporter = func(v any, i int) any { switch v := v.(*MultiMemberGroupInitialMemberAnnounced); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[18].Exporter = func(v any, i int) any { switch v := v.(*GroupAddAdditionalRendezvousSeed); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[19].Exporter = func(v any, i int) any { switch v := v.(*GroupRemoveAdditionalRendezvousSeed); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[20].Exporter = func(v any, i int) any { switch v := v.(*AccountGroupJoined); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[21].Exporter = func(v any, i int) any { switch v := v.(*AccountGroupLeft); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[22].Exporter = func(v any, i int) any { switch v := v.(*AccountContactRequestDisabled); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[23].Exporter = func(v any, i int) any { switch v := v.(*AccountContactRequestEnabled); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[24].Exporter = func(v any, i int) any { switch v := v.(*AccountContactRequestReferenceReset); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[25].Exporter = func(v any, i int) any { switch v := v.(*AccountContactRequestOutgoingEnqueued); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[26].Exporter = func(v any, i int) any { switch v := v.(*AccountContactRequestOutgoingSent); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[27].Exporter = func(v any, i int) any { switch v := v.(*AccountContactRequestIncomingReceived); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[28].Exporter = func(v any, i int) any { switch v := v.(*AccountContactRequestIncomingDiscarded); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[29].Exporter = func(v any, i int) any { switch v := v.(*AccountContactRequestIncomingAccepted); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[30].Exporter = func(v any, i int) any { switch v := v.(*AccountContactBlocked); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[31].Exporter = func(v any, i int) any { switch v := v.(*AccountContactUnblocked); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[32].Exporter = func(v any, i int) any { switch v := v.(*GroupReplicating); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[33].Exporter = func(v any, i int) any { switch v := v.(*ServiceExportData); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[34].Exporter = func(v any, i int) any { switch v := v.(*ServiceGetConfiguration); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[35].Exporter = func(v any, i int) any { switch v := v.(*ContactRequestReference); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[36].Exporter = func(v any, i int) any { switch v := v.(*ContactRequestDisable); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[37].Exporter = func(v any, i int) any { switch v := v.(*ContactRequestEnable); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[38].Exporter = func(v any, i int) any { switch v := v.(*ContactRequestResetReference); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[39].Exporter = func(v any, i int) any { switch v := v.(*ContactRequestSend); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[40].Exporter = func(v any, i int) any { switch v := v.(*ContactRequestAccept); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[41].Exporter = func(v any, i int) any { switch v := v.(*ContactRequestDiscard); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[42].Exporter = func(v any, i int) any { switch v := v.(*ShareContact); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[43].Exporter = func(v any, i int) any { switch v := v.(*DecodeContact); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[44].Exporter = func(v any, i int) any { switch v := v.(*ContactBlock); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[45].Exporter = func(v any, i int) any { switch v := v.(*ContactUnblock); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[46].Exporter = func(v any, i int) any { switch v := v.(*ContactAliasKeySend); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[47].Exporter = func(v any, i int) any { switch v := v.(*MultiMemberGroupCreate); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[48].Exporter = func(v any, i int) any { switch v := v.(*MultiMemberGroupJoin); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[49].Exporter = func(v any, i int) any { switch v := v.(*MultiMemberGroupLeave); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[50].Exporter = func(v any, i int) any { switch v := v.(*MultiMemberGroupAliasResolverDisclose); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[51].Exporter = func(v any, i int) any { switch v := v.(*MultiMemberGroupAdminRoleGrant); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[52].Exporter = func(v any, i int) any { switch v := v.(*MultiMemberGroupInvitationCreate); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[53].Exporter = func(v any, i int) any { switch v := v.(*AppMetadataSend); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[54].Exporter = func(v any, i int) any { switch v := v.(*AppMessageSend); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[55].Exporter = func(v any, i int) any { switch v := v.(*GroupMetadataEvent); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[56].Exporter = func(v any, i int) any { switch v := v.(*GroupMessageEvent); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[57].Exporter = func(v any, i int) any { switch v := v.(*GroupMetadataList); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[58].Exporter = func(v any, i int) any { switch v := v.(*GroupMessageList); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[59].Exporter = func(v any, i int) any { switch v := v.(*GroupInfo); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[60].Exporter = func(v any, i int) any { switch v := v.(*ActivateGroup); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[61].Exporter = func(v any, i int) any { switch v := v.(*DeactivateGroup); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[62].Exporter = func(v any, i int) any { switch v := v.(*GroupDeviceStatus); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[63].Exporter = func(v any, i int) any { switch v := v.(*DebugListGroups); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[64].Exporter = func(v any, i int) any { switch v := v.(*DebugInspectGroupStore); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[65].Exporter = func(v any, i int) any { switch v := v.(*DebugGroup); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[66].Exporter = func(v any, i int) any { switch v := v.(*ShareableContact); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[67].Exporter = func(v any, i int) any { switch v := v.(*ServiceTokenSupportedService); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[68].Exporter = func(v any, i int) any { switch v := v.(*ServiceToken); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[69].Exporter = func(v any, i int) any { switch v := v.(*CredentialVerificationServiceInitFlow); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[70].Exporter = func(v any, i int) any { switch v := v.(*CredentialVerificationServiceCompleteFlow); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[71].Exporter = func(v any, i int) any { switch v := v.(*VerifiedCredentialsList); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[72].Exporter = func(v any, i int) any { switch v := v.(*ReplicationServiceRegisterGroup); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[73].Exporter = func(v any, i int) any { switch v := v.(*ReplicationServiceReplicateGroup); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[74].Exporter = func(v any, i int) any { switch v := v.(*SystemInfo); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[75].Exporter = func(v any, i int) any { switch v := v.(*PeerList); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[76].Exporter = func(v any, i int) any { switch v := v.(*Progress); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[77].Exporter = func(v any, i int) any { switch v := v.(*OutOfStoreMessage); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[78].Exporter = func(v any, i int) any { switch v := v.(*OutOfStoreMessageEnvelope); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[79].Exporter = func(v any, i int) any { switch v := v.(*OutOfStoreReceive); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[80].Exporter = func(v any, i int) any { switch v := v.(*OutOfStoreSeal); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[81].Exporter = func(v any, i int) any { switch v := v.(*AccountVerifiedCredentialRegistered); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[82].Exporter = func(v any, i int) any { switch v := v.(*FirstLastCounters); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[83].Exporter = func(v any, i int) any { switch v := v.(*OrbitDBMessageHeads); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[84].Exporter = func(v any, i int) any { switch v := v.(*RefreshContactRequest); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[86].Exporter = func(v any, i int) any { switch v := v.(*ServiceExportData_Request); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[87].Exporter = func(v any, i int) any { switch v := v.(*ServiceExportData_Reply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[88].Exporter = func(v any, i int) any { switch v := v.(*ServiceGetConfiguration_Request); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[89].Exporter = func(v any, i int) any { switch v := v.(*ServiceGetConfiguration_Reply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[90].Exporter = func(v any, i int) any { switch v := v.(*ContactRequestReference_Request); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[91].Exporter = func(v any, i int) any { switch v := v.(*ContactRequestReference_Reply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[92].Exporter = func(v any, i int) any { switch v := v.(*ContactRequestDisable_Request); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[93].Exporter = func(v any, i int) any { switch v := v.(*ContactRequestDisable_Reply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[94].Exporter = func(v any, i int) any { switch v := v.(*ContactRequestEnable_Request); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[95].Exporter = func(v any, i int) any { switch v := v.(*ContactRequestEnable_Reply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[96].Exporter = func(v any, i int) any { switch v := v.(*ContactRequestResetReference_Request); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[97].Exporter = func(v any, i int) any { switch v := v.(*ContactRequestResetReference_Reply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[98].Exporter = func(v any, i int) any { switch v := v.(*ContactRequestSend_Request); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[99].Exporter = func(v any, i int) any { switch v := v.(*ContactRequestSend_Reply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[100].Exporter = func(v any, i int) any { switch v := v.(*ContactRequestAccept_Request); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[101].Exporter = func(v any, i int) any { switch v := v.(*ContactRequestAccept_Reply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[102].Exporter = func(v any, i int) any { switch v := v.(*ContactRequestDiscard_Request); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[103].Exporter = func(v any, i int) any { switch v := v.(*ContactRequestDiscard_Reply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[104].Exporter = func(v any, i int) any { switch v := v.(*ShareContact_Request); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[105].Exporter = func(v any, i int) any { switch v := v.(*ShareContact_Reply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[106].Exporter = func(v any, i int) any { switch v := v.(*DecodeContact_Request); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[107].Exporter = func(v any, i int) any { switch v := v.(*DecodeContact_Reply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[108].Exporter = func(v any, i int) any { switch v := v.(*ContactBlock_Request); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[109].Exporter = func(v any, i int) any { switch v := v.(*ContactBlock_Reply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[110].Exporter = func(v any, i int) any { switch v := v.(*ContactUnblock_Request); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[111].Exporter = func(v any, i int) any { switch v := v.(*ContactUnblock_Reply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[112].Exporter = func(v any, i int) any { switch v := v.(*ContactAliasKeySend_Request); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[113].Exporter = func(v any, i int) any { switch v := v.(*ContactAliasKeySend_Reply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[114].Exporter = func(v any, i int) any { switch v := v.(*MultiMemberGroupCreate_Request); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[115].Exporter = func(v any, i int) any { switch v := v.(*MultiMemberGroupCreate_Reply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[116].Exporter = func(v any, i int) any { switch v := v.(*MultiMemberGroupJoin_Request); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[117].Exporter = func(v any, i int) any { switch v := v.(*MultiMemberGroupJoin_Reply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[118].Exporter = func(v any, i int) any { switch v := v.(*MultiMemberGroupLeave_Request); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[119].Exporter = func(v any, i int) any { switch v := v.(*MultiMemberGroupLeave_Reply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[120].Exporter = func(v any, i int) any { switch v := v.(*MultiMemberGroupAliasResolverDisclose_Request); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[121].Exporter = func(v any, i int) any { switch v := v.(*MultiMemberGroupAliasResolverDisclose_Reply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[122].Exporter = func(v any, i int) any { switch v := v.(*MultiMemberGroupAdminRoleGrant_Request); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[123].Exporter = func(v any, i int) any { switch v := v.(*MultiMemberGroupAdminRoleGrant_Reply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[124].Exporter = func(v any, i int) any { switch v := v.(*MultiMemberGroupInvitationCreate_Request); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[125].Exporter = func(v any, i int) any { switch v := v.(*MultiMemberGroupInvitationCreate_Reply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[126].Exporter = func(v any, i int) any { switch v := v.(*AppMetadataSend_Request); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[127].Exporter = func(v any, i int) any { switch v := v.(*AppMetadataSend_Reply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[128].Exporter = func(v any, i int) any { switch v := v.(*AppMessageSend_Request); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[129].Exporter = func(v any, i int) any { switch v := v.(*AppMessageSend_Reply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[130].Exporter = func(v any, i int) any { switch v := v.(*GroupMetadataList_Request); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[131].Exporter = func(v any, i int) any { switch v := v.(*GroupMessageList_Request); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[132].Exporter = func(v any, i int) any { switch v := v.(*GroupInfo_Request); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[133].Exporter = func(v any, i int) any { switch v := v.(*GroupInfo_Reply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[134].Exporter = func(v any, i int) any { switch v := v.(*ActivateGroup_Request); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[135].Exporter = func(v any, i int) any { switch v := v.(*ActivateGroup_Reply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[136].Exporter = func(v any, i int) any { switch v := v.(*DeactivateGroup_Request); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[137].Exporter = func(v any, i int) any { switch v := v.(*DeactivateGroup_Reply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[138].Exporter = func(v any, i int) any { switch v := v.(*GroupDeviceStatus_Request); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[139].Exporter = func(v any, i int) any { switch v := v.(*GroupDeviceStatus_Reply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[140].Exporter = func(v any, i int) any { switch v := v.(*GroupDeviceStatus_Reply_PeerConnected); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[141].Exporter = func(v any, i int) any { switch v := v.(*GroupDeviceStatus_Reply_PeerReconnecting); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[142].Exporter = func(v any, i int) any { switch v := v.(*GroupDeviceStatus_Reply_PeerDisconnected); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[143].Exporter = func(v any, i int) any { switch v := v.(*DebugListGroups_Request); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[144].Exporter = func(v any, i int) any { switch v := v.(*DebugListGroups_Reply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[145].Exporter = func(v any, i int) any { switch v := v.(*DebugInspectGroupStore_Request); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[146].Exporter = func(v any, i int) any { switch v := v.(*DebugInspectGroupStore_Reply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[147].Exporter = func(v any, i int) any { switch v := v.(*DebugGroup_Request); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[148].Exporter = func(v any, i int) any { switch v := v.(*DebugGroup_Reply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[149].Exporter = func(v any, i int) any { switch v := v.(*CredentialVerificationServiceInitFlow_Request); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[150].Exporter = func(v any, i int) any { switch v := v.(*CredentialVerificationServiceInitFlow_Reply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[151].Exporter = func(v any, i int) any { switch v := v.(*CredentialVerificationServiceCompleteFlow_Request); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[152].Exporter = func(v any, i int) any { switch v := v.(*CredentialVerificationServiceCompleteFlow_Reply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[153].Exporter = func(v any, i int) any { switch v := v.(*VerifiedCredentialsList_Request); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[154].Exporter = func(v any, i int) any { switch v := v.(*VerifiedCredentialsList_Reply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[155].Exporter = func(v any, i int) any { switch v := v.(*ReplicationServiceRegisterGroup_Request); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[156].Exporter = func(v any, i int) any { switch v := v.(*ReplicationServiceRegisterGroup_Reply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[157].Exporter = func(v any, i int) any { switch v := v.(*ReplicationServiceReplicateGroup_Request); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[158].Exporter = func(v any, i int) any { switch v := v.(*ReplicationServiceReplicateGroup_Reply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[159].Exporter = func(v any, i int) any { switch v := v.(*SystemInfo_Request); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[160].Exporter = func(v any, i int) any { switch v := v.(*SystemInfo_Reply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[161].Exporter = func(v any, i int) any { switch v := v.(*SystemInfo_OrbitDB); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[162].Exporter = func(v any, i int) any { switch v := v.(*SystemInfo_P2P); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[163].Exporter = func(v any, i int) any { switch v := v.(*SystemInfo_Process); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[164].Exporter = func(v any, i int) any { switch v := v.(*SystemInfo_OrbitDB_ReplicationStatus); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[165].Exporter = func(v any, i int) any { switch v := v.(*PeerList_Request); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[166].Exporter = func(v any, i int) any { switch v := v.(*PeerList_Reply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[167].Exporter = func(v any, i int) any { switch v := v.(*PeerList_Peer); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[168].Exporter = func(v any, i int) any { switch v := v.(*PeerList_Route); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[169].Exporter = func(v any, i int) any { switch v := v.(*PeerList_Stream); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[170].Exporter = func(v any, i int) any { switch v := v.(*OutOfStoreReceive_Request); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[171].Exporter = func(v any, i int) any { switch v := v.(*OutOfStoreReceive_Reply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[172].Exporter = func(v any, i int) any { switch v := v.(*OutOfStoreSeal_Request); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[173].Exporter = func(v any, i int) any { switch v := v.(*OutOfStoreSeal_Reply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[174].Exporter = func(v any, i int) any { switch v := v.(*OrbitDBMessageHeads_Box); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[175].Exporter = func(v any, i int) any { switch v := v.(*RefreshContactRequest_Peer); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[176].Exporter = func(v any, i int) any { switch v := v.(*RefreshContactRequest_Request); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_protocoltypes_proto_msgTypes[177].Exporter = func(v any, i int) any { switch v := v.(*RefreshContactRequest_Reply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_protocoltypes_proto_rawDesc, NumEnums: 9, NumMessages: 178, NumExtensions: 0, NumServices: 1, }, GoTypes: file_protocoltypes_proto_goTypes, DependencyIndexes: file_protocoltypes_proto_depIdxs, EnumInfos: file_protocoltypes_proto_enumTypes, MessageInfos: file_protocoltypes_proto_msgTypes, }.Build() File_protocoltypes_proto = out.File file_protocoltypes_proto_rawDesc = nil file_protocoltypes_proto_goTypes = nil file_protocoltypes_proto_depIdxs = nil } ================================================ FILE: pkg/protocoltypes/protocoltypes.pb.gw.go ================================================ // Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. // source: protocoltypes.proto /* Package protocoltypes is a reverse proxy. It translates gRPC into RESTful JSON APIs. */ package protocoltypes import ( "context" "io" "net/http" "github.com/golang/protobuf/descriptor" "github.com/golang/protobuf/proto" "github.com/grpc-ecosystem/grpc-gateway/runtime" "github.com/grpc-ecosystem/grpc-gateway/utilities" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/grpclog" "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" ) // Suppress "imported and not used" errors var _ codes.Code var _ io.Reader var _ status.Status var _ = runtime.String var _ = utilities.NewDoubleArray var _ = descriptor.ForMessage var _ = metadata.Join func request_ProtocolService_ServiceExportData_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (ProtocolService_ServiceExportDataClient, runtime.ServerMetadata, error) { var protoReq ServiceExportData_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } stream, err := client.ServiceExportData(ctx, &protoReq) if err != nil { return nil, metadata, err } header, err := stream.Header() if err != nil { return nil, metadata, err } metadata.HeaderMD = header return stream, metadata, nil } func request_ProtocolService_ServiceGetConfiguration_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq ServiceGetConfiguration_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := client.ServiceGetConfiguration(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } func 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) { var protoReq ServiceGetConfiguration_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := server.ServiceGetConfiguration(ctx, &protoReq) return msg, metadata, err } func request_ProtocolService_ContactRequestReference_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq ContactRequestReference_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := client.ContactRequestReference(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } func 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) { var protoReq ContactRequestReference_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := server.ContactRequestReference(ctx, &protoReq) return msg, metadata, err } func request_ProtocolService_ContactRequestDisable_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq ContactRequestDisable_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := client.ContactRequestDisable(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } func 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) { var protoReq ContactRequestDisable_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := server.ContactRequestDisable(ctx, &protoReq) return msg, metadata, err } func request_ProtocolService_ContactRequestEnable_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq ContactRequestEnable_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := client.ContactRequestEnable(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } func 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) { var protoReq ContactRequestEnable_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := server.ContactRequestEnable(ctx, &protoReq) return msg, metadata, err } func request_ProtocolService_ContactRequestResetReference_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq ContactRequestResetReference_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := client.ContactRequestResetReference(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } func 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) { var protoReq ContactRequestResetReference_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := server.ContactRequestResetReference(ctx, &protoReq) return msg, metadata, err } func request_ProtocolService_ContactRequestSend_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq ContactRequestSend_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := client.ContactRequestSend(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } func 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) { var protoReq ContactRequestSend_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := server.ContactRequestSend(ctx, &protoReq) return msg, metadata, err } func request_ProtocolService_ContactRequestAccept_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq ContactRequestAccept_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := client.ContactRequestAccept(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } func 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) { var protoReq ContactRequestAccept_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := server.ContactRequestAccept(ctx, &protoReq) return msg, metadata, err } func request_ProtocolService_ContactRequestDiscard_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq ContactRequestDiscard_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := client.ContactRequestDiscard(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } func 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) { var protoReq ContactRequestDiscard_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := server.ContactRequestDiscard(ctx, &protoReq) return msg, metadata, err } func request_ProtocolService_ShareContact_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq ShareContact_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := client.ShareContact(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } func 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) { var protoReq ShareContact_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := server.ShareContact(ctx, &protoReq) return msg, metadata, err } func request_ProtocolService_DecodeContact_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq DecodeContact_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := client.DecodeContact(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } func 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) { var protoReq DecodeContact_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := server.DecodeContact(ctx, &protoReq) return msg, metadata, err } func request_ProtocolService_ContactBlock_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq ContactBlock_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := client.ContactBlock(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } func 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) { var protoReq ContactBlock_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := server.ContactBlock(ctx, &protoReq) return msg, metadata, err } func request_ProtocolService_ContactUnblock_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq ContactUnblock_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := client.ContactUnblock(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } func 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) { var protoReq ContactUnblock_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := server.ContactUnblock(ctx, &protoReq) return msg, metadata, err } func request_ProtocolService_ContactAliasKeySend_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq ContactAliasKeySend_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := client.ContactAliasKeySend(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } func 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) { var protoReq ContactAliasKeySend_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := server.ContactAliasKeySend(ctx, &protoReq) return msg, metadata, err } func request_ProtocolService_MultiMemberGroupCreate_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq MultiMemberGroupCreate_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := client.MultiMemberGroupCreate(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } func 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) { var protoReq MultiMemberGroupCreate_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := server.MultiMemberGroupCreate(ctx, &protoReq) return msg, metadata, err } func request_ProtocolService_MultiMemberGroupJoin_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq MultiMemberGroupJoin_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := client.MultiMemberGroupJoin(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } func 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) { var protoReq MultiMemberGroupJoin_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := server.MultiMemberGroupJoin(ctx, &protoReq) return msg, metadata, err } func request_ProtocolService_MultiMemberGroupLeave_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq MultiMemberGroupLeave_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := client.MultiMemberGroupLeave(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } func 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) { var protoReq MultiMemberGroupLeave_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := server.MultiMemberGroupLeave(ctx, &protoReq) return msg, metadata, err } func request_ProtocolService_MultiMemberGroupAliasResolverDisclose_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq MultiMemberGroupAliasResolverDisclose_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := client.MultiMemberGroupAliasResolverDisclose(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } func 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) { var protoReq MultiMemberGroupAliasResolverDisclose_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := server.MultiMemberGroupAliasResolverDisclose(ctx, &protoReq) return msg, metadata, err } func request_ProtocolService_MultiMemberGroupAdminRoleGrant_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq MultiMemberGroupAdminRoleGrant_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := client.MultiMemberGroupAdminRoleGrant(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } func 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) { var protoReq MultiMemberGroupAdminRoleGrant_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := server.MultiMemberGroupAdminRoleGrant(ctx, &protoReq) return msg, metadata, err } func request_ProtocolService_MultiMemberGroupInvitationCreate_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq MultiMemberGroupInvitationCreate_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := client.MultiMemberGroupInvitationCreate(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } func 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) { var protoReq MultiMemberGroupInvitationCreate_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := server.MultiMemberGroupInvitationCreate(ctx, &protoReq) return msg, metadata, err } func request_ProtocolService_AppMetadataSend_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq AppMetadataSend_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := client.AppMetadataSend(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } func 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) { var protoReq AppMetadataSend_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := server.AppMetadataSend(ctx, &protoReq) return msg, metadata, err } func request_ProtocolService_AppMessageSend_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq AppMessageSend_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := client.AppMessageSend(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } func 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) { var protoReq AppMessageSend_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := server.AppMessageSend(ctx, &protoReq) return msg, metadata, err } func request_ProtocolService_GroupMetadataList_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (ProtocolService_GroupMetadataListClient, runtime.ServerMetadata, error) { var protoReq GroupMetadataList_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } stream, err := client.GroupMetadataList(ctx, &protoReq) if err != nil { return nil, metadata, err } header, err := stream.Header() if err != nil { return nil, metadata, err } metadata.HeaderMD = header return stream, metadata, nil } func request_ProtocolService_GroupMessageList_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (ProtocolService_GroupMessageListClient, runtime.ServerMetadata, error) { var protoReq GroupMessageList_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } stream, err := client.GroupMessageList(ctx, &protoReq) if err != nil { return nil, metadata, err } header, err := stream.Header() if err != nil { return nil, metadata, err } metadata.HeaderMD = header return stream, metadata, nil } func request_ProtocolService_GroupInfo_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq GroupInfo_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := client.GroupInfo(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } func 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) { var protoReq GroupInfo_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := server.GroupInfo(ctx, &protoReq) return msg, metadata, err } func request_ProtocolService_ActivateGroup_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq ActivateGroup_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := client.ActivateGroup(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } func 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) { var protoReq ActivateGroup_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := server.ActivateGroup(ctx, &protoReq) return msg, metadata, err } func request_ProtocolService_DeactivateGroup_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq DeactivateGroup_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := client.DeactivateGroup(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } func 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) { var protoReq DeactivateGroup_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := server.DeactivateGroup(ctx, &protoReq) return msg, metadata, err } func request_ProtocolService_GroupDeviceStatus_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (ProtocolService_GroupDeviceStatusClient, runtime.ServerMetadata, error) { var protoReq GroupDeviceStatus_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } stream, err := client.GroupDeviceStatus(ctx, &protoReq) if err != nil { return nil, metadata, err } header, err := stream.Header() if err != nil { return nil, metadata, err } metadata.HeaderMD = header return stream, metadata, nil } func request_ProtocolService_DebugListGroups_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (ProtocolService_DebugListGroupsClient, runtime.ServerMetadata, error) { var protoReq DebugListGroups_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } stream, err := client.DebugListGroups(ctx, &protoReq) if err != nil { return nil, metadata, err } header, err := stream.Header() if err != nil { return nil, metadata, err } metadata.HeaderMD = header return stream, metadata, nil } func request_ProtocolService_DebugInspectGroupStore_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (ProtocolService_DebugInspectGroupStoreClient, runtime.ServerMetadata, error) { var protoReq DebugInspectGroupStore_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } stream, err := client.DebugInspectGroupStore(ctx, &protoReq) if err != nil { return nil, metadata, err } header, err := stream.Header() if err != nil { return nil, metadata, err } metadata.HeaderMD = header return stream, metadata, nil } func request_ProtocolService_DebugGroup_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq DebugGroup_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := client.DebugGroup(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } func 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) { var protoReq DebugGroup_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := server.DebugGroup(ctx, &protoReq) return msg, metadata, err } func request_ProtocolService_SystemInfo_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq SystemInfo_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := client.SystemInfo(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } func 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) { var protoReq SystemInfo_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := server.SystemInfo(ctx, &protoReq) return msg, metadata, err } func request_ProtocolService_CredentialVerificationServiceInitFlow_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq CredentialVerificationServiceInitFlow_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := client.CredentialVerificationServiceInitFlow(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } func 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) { var protoReq CredentialVerificationServiceInitFlow_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := server.CredentialVerificationServiceInitFlow(ctx, &protoReq) return msg, metadata, err } func request_ProtocolService_CredentialVerificationServiceCompleteFlow_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq CredentialVerificationServiceCompleteFlow_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := client.CredentialVerificationServiceCompleteFlow(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } func 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) { var protoReq CredentialVerificationServiceCompleteFlow_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := server.CredentialVerificationServiceCompleteFlow(ctx, &protoReq) return msg, metadata, err } func request_ProtocolService_VerifiedCredentialsList_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (ProtocolService_VerifiedCredentialsListClient, runtime.ServerMetadata, error) { var protoReq VerifiedCredentialsList_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } stream, err := client.VerifiedCredentialsList(ctx, &protoReq) if err != nil { return nil, metadata, err } header, err := stream.Header() if err != nil { return nil, metadata, err } metadata.HeaderMD = header return stream, metadata, nil } func request_ProtocolService_ReplicationServiceRegisterGroup_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq ReplicationServiceRegisterGroup_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := client.ReplicationServiceRegisterGroup(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } func 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) { var protoReq ReplicationServiceRegisterGroup_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := server.ReplicationServiceRegisterGroup(ctx, &protoReq) return msg, metadata, err } func request_ProtocolService_PeerList_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq PeerList_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := client.PeerList(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } func 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) { var protoReq PeerList_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := server.PeerList(ctx, &protoReq) return msg, metadata, err } func request_ProtocolService_OutOfStoreReceive_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq OutOfStoreReceive_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := client.OutOfStoreReceive(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } func 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) { var protoReq OutOfStoreReceive_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := server.OutOfStoreReceive(ctx, &protoReq) return msg, metadata, err } func request_ProtocolService_OutOfStoreSeal_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq OutOfStoreSeal_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := client.OutOfStoreSeal(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } func 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) { var protoReq OutOfStoreSeal_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := server.OutOfStoreSeal(ctx, &protoReq) return msg, metadata, err } func request_ProtocolService_RefreshContactRequest_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq RefreshContactRequest_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := client.RefreshContactRequest(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } func 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) { var protoReq RefreshContactRequest_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := server.RefreshContactRequest(ctx, &protoReq) return msg, metadata, err } // RegisterProtocolServiceHandlerServer registers the http handlers for service ProtocolService to "mux". // UnaryRPC :call ProtocolServiceServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. // Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterProtocolServiceHandlerFromEndpoint instead. func RegisterProtocolServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server ProtocolServiceServer) error { mux.Handle("POST", pattern_ProtocolService_ServiceExportData_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { err := status.Error(codes.Unimplemented, "streaming calls are not yet supported in the in-process transport") _, outboundMarshaler := runtime.MarshalerForRequest(mux, req) runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return }) mux.Handle("POST", pattern_ProtocolService_ServiceGetConfiguration_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := local_request_ProtocolService_ServiceGetConfiguration_0(rctx, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_ServiceGetConfiguration_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_ContactRequestReference_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := local_request_ProtocolService_ContactRequestReference_0(rctx, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_ContactRequestReference_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_ContactRequestDisable_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := local_request_ProtocolService_ContactRequestDisable_0(rctx, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_ContactRequestDisable_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_ContactRequestEnable_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := local_request_ProtocolService_ContactRequestEnable_0(rctx, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_ContactRequestEnable_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_ContactRequestResetReference_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := local_request_ProtocolService_ContactRequestResetReference_0(rctx, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_ContactRequestResetReference_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_ContactRequestSend_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := local_request_ProtocolService_ContactRequestSend_0(rctx, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_ContactRequestSend_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_ContactRequestAccept_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := local_request_ProtocolService_ContactRequestAccept_0(rctx, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_ContactRequestAccept_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_ContactRequestDiscard_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := local_request_ProtocolService_ContactRequestDiscard_0(rctx, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_ContactRequestDiscard_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_ShareContact_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := local_request_ProtocolService_ShareContact_0(rctx, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_ShareContact_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_DecodeContact_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := local_request_ProtocolService_DecodeContact_0(rctx, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_DecodeContact_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_ContactBlock_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := local_request_ProtocolService_ContactBlock_0(rctx, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_ContactBlock_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_ContactUnblock_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := local_request_ProtocolService_ContactUnblock_0(rctx, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_ContactUnblock_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_ContactAliasKeySend_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := local_request_ProtocolService_ContactAliasKeySend_0(rctx, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_ContactAliasKeySend_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_MultiMemberGroupCreate_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := local_request_ProtocolService_MultiMemberGroupCreate_0(rctx, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_MultiMemberGroupCreate_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_MultiMemberGroupJoin_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := local_request_ProtocolService_MultiMemberGroupJoin_0(rctx, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_MultiMemberGroupJoin_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_MultiMemberGroupLeave_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := local_request_ProtocolService_MultiMemberGroupLeave_0(rctx, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_MultiMemberGroupLeave_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_MultiMemberGroupAliasResolverDisclose_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := local_request_ProtocolService_MultiMemberGroupAliasResolverDisclose_0(rctx, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_MultiMemberGroupAliasResolverDisclose_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_MultiMemberGroupAdminRoleGrant_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := local_request_ProtocolService_MultiMemberGroupAdminRoleGrant_0(rctx, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_MultiMemberGroupAdminRoleGrant_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_MultiMemberGroupInvitationCreate_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := local_request_ProtocolService_MultiMemberGroupInvitationCreate_0(rctx, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_MultiMemberGroupInvitationCreate_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_AppMetadataSend_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := local_request_ProtocolService_AppMetadataSend_0(rctx, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_AppMetadataSend_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_AppMessageSend_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := local_request_ProtocolService_AppMessageSend_0(rctx, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_AppMessageSend_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_GroupMetadataList_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { err := status.Error(codes.Unimplemented, "streaming calls are not yet supported in the in-process transport") _, outboundMarshaler := runtime.MarshalerForRequest(mux, req) runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return }) mux.Handle("POST", pattern_ProtocolService_GroupMessageList_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { err := status.Error(codes.Unimplemented, "streaming calls are not yet supported in the in-process transport") _, outboundMarshaler := runtime.MarshalerForRequest(mux, req) runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return }) mux.Handle("POST", pattern_ProtocolService_GroupInfo_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := local_request_ProtocolService_GroupInfo_0(rctx, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_GroupInfo_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_ActivateGroup_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := local_request_ProtocolService_ActivateGroup_0(rctx, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_ActivateGroup_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_DeactivateGroup_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := local_request_ProtocolService_DeactivateGroup_0(rctx, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_DeactivateGroup_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_GroupDeviceStatus_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { err := status.Error(codes.Unimplemented, "streaming calls are not yet supported in the in-process transport") _, outboundMarshaler := runtime.MarshalerForRequest(mux, req) runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return }) mux.Handle("POST", pattern_ProtocolService_DebugListGroups_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { err := status.Error(codes.Unimplemented, "streaming calls are not yet supported in the in-process transport") _, outboundMarshaler := runtime.MarshalerForRequest(mux, req) runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return }) mux.Handle("POST", pattern_ProtocolService_DebugInspectGroupStore_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { err := status.Error(codes.Unimplemented, "streaming calls are not yet supported in the in-process transport") _, outboundMarshaler := runtime.MarshalerForRequest(mux, req) runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return }) mux.Handle("POST", pattern_ProtocolService_DebugGroup_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := local_request_ProtocolService_DebugGroup_0(rctx, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_DebugGroup_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_SystemInfo_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := local_request_ProtocolService_SystemInfo_0(rctx, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_SystemInfo_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_CredentialVerificationServiceInitFlow_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := local_request_ProtocolService_CredentialVerificationServiceInitFlow_0(rctx, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_CredentialVerificationServiceInitFlow_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_CredentialVerificationServiceCompleteFlow_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := local_request_ProtocolService_CredentialVerificationServiceCompleteFlow_0(rctx, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_CredentialVerificationServiceCompleteFlow_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_VerifiedCredentialsList_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { err := status.Error(codes.Unimplemented, "streaming calls are not yet supported in the in-process transport") _, outboundMarshaler := runtime.MarshalerForRequest(mux, req) runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return }) mux.Handle("POST", pattern_ProtocolService_ReplicationServiceRegisterGroup_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := local_request_ProtocolService_ReplicationServiceRegisterGroup_0(rctx, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_ReplicationServiceRegisterGroup_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_PeerList_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := local_request_ProtocolService_PeerList_0(rctx, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_PeerList_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_OutOfStoreReceive_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := local_request_ProtocolService_OutOfStoreReceive_0(rctx, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_OutOfStoreReceive_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_OutOfStoreSeal_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := local_request_ProtocolService_OutOfStoreSeal_0(rctx, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_OutOfStoreSeal_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_RefreshContactRequest_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := local_request_ProtocolService_RefreshContactRequest_0(rctx, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_RefreshContactRequest_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) return nil } // RegisterProtocolServiceHandlerFromEndpoint is same as RegisterProtocolServiceHandler but // automatically dials to "endpoint" and closes the connection when "ctx" gets done. func RegisterProtocolServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { conn, err := grpc.Dial(endpoint, opts...) if err != nil { return err } defer func() { if err != nil { if cerr := conn.Close(); cerr != nil { grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) } return } go func() { <-ctx.Done() if cerr := conn.Close(); cerr != nil { grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) } }() }() return RegisterProtocolServiceHandler(ctx, mux, conn) } // RegisterProtocolServiceHandler registers the http handlers for service ProtocolService to "mux". // The handlers forward requests to the grpc endpoint over "conn". func RegisterProtocolServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { return RegisterProtocolServiceHandlerClient(ctx, mux, NewProtocolServiceClient(conn)) } // RegisterProtocolServiceHandlerClient registers the http handlers for service ProtocolService // to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "ProtocolServiceClient". // Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "ProtocolServiceClient" // doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in // "ProtocolServiceClient" to call the correct interceptors. func RegisterProtocolServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client ProtocolServiceClient) error { mux.Handle("POST", pattern_ProtocolService_ServiceExportData_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_ProtocolService_ServiceExportData_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_ServiceExportData_0(ctx, mux, outboundMarshaler, w, req, func() (proto.Message, error) { return resp.Recv() }, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_ServiceGetConfiguration_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_ProtocolService_ServiceGetConfiguration_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_ServiceGetConfiguration_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_ContactRequestReference_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_ProtocolService_ContactRequestReference_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_ContactRequestReference_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_ContactRequestDisable_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_ProtocolService_ContactRequestDisable_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_ContactRequestDisable_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_ContactRequestEnable_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_ProtocolService_ContactRequestEnable_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_ContactRequestEnable_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_ContactRequestResetReference_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_ProtocolService_ContactRequestResetReference_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_ContactRequestResetReference_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_ContactRequestSend_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_ProtocolService_ContactRequestSend_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_ContactRequestSend_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_ContactRequestAccept_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_ProtocolService_ContactRequestAccept_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_ContactRequestAccept_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_ContactRequestDiscard_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_ProtocolService_ContactRequestDiscard_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_ContactRequestDiscard_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_ShareContact_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_ProtocolService_ShareContact_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_ShareContact_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_DecodeContact_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_ProtocolService_DecodeContact_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_DecodeContact_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_ContactBlock_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_ProtocolService_ContactBlock_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_ContactBlock_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_ContactUnblock_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_ProtocolService_ContactUnblock_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_ContactUnblock_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_ContactAliasKeySend_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_ProtocolService_ContactAliasKeySend_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_ContactAliasKeySend_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_MultiMemberGroupCreate_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_ProtocolService_MultiMemberGroupCreate_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_MultiMemberGroupCreate_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_MultiMemberGroupJoin_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_ProtocolService_MultiMemberGroupJoin_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_MultiMemberGroupJoin_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_MultiMemberGroupLeave_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_ProtocolService_MultiMemberGroupLeave_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_MultiMemberGroupLeave_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_MultiMemberGroupAliasResolverDisclose_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_ProtocolService_MultiMemberGroupAliasResolverDisclose_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_MultiMemberGroupAliasResolverDisclose_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_MultiMemberGroupAdminRoleGrant_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_ProtocolService_MultiMemberGroupAdminRoleGrant_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_MultiMemberGroupAdminRoleGrant_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_MultiMemberGroupInvitationCreate_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_ProtocolService_MultiMemberGroupInvitationCreate_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_MultiMemberGroupInvitationCreate_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_AppMetadataSend_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_ProtocolService_AppMetadataSend_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_AppMetadataSend_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_AppMessageSend_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_ProtocolService_AppMessageSend_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_AppMessageSend_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_GroupMetadataList_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_ProtocolService_GroupMetadataList_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_GroupMetadataList_0(ctx, mux, outboundMarshaler, w, req, func() (proto.Message, error) { return resp.Recv() }, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_GroupMessageList_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_ProtocolService_GroupMessageList_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_GroupMessageList_0(ctx, mux, outboundMarshaler, w, req, func() (proto.Message, error) { return resp.Recv() }, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_GroupInfo_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_ProtocolService_GroupInfo_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_GroupInfo_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_ActivateGroup_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_ProtocolService_ActivateGroup_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_ActivateGroup_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_DeactivateGroup_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_ProtocolService_DeactivateGroup_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_DeactivateGroup_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_GroupDeviceStatus_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_ProtocolService_GroupDeviceStatus_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_GroupDeviceStatus_0(ctx, mux, outboundMarshaler, w, req, func() (proto.Message, error) { return resp.Recv() }, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_DebugListGroups_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_ProtocolService_DebugListGroups_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_DebugListGroups_0(ctx, mux, outboundMarshaler, w, req, func() (proto.Message, error) { return resp.Recv() }, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_DebugInspectGroupStore_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_ProtocolService_DebugInspectGroupStore_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_DebugInspectGroupStore_0(ctx, mux, outboundMarshaler, w, req, func() (proto.Message, error) { return resp.Recv() }, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_DebugGroup_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_ProtocolService_DebugGroup_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_DebugGroup_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_SystemInfo_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_ProtocolService_SystemInfo_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_SystemInfo_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_CredentialVerificationServiceInitFlow_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_ProtocolService_CredentialVerificationServiceInitFlow_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_CredentialVerificationServiceInitFlow_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_CredentialVerificationServiceCompleteFlow_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_ProtocolService_CredentialVerificationServiceCompleteFlow_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_CredentialVerificationServiceCompleteFlow_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_VerifiedCredentialsList_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_ProtocolService_VerifiedCredentialsList_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_VerifiedCredentialsList_0(ctx, mux, outboundMarshaler, w, req, func() (proto.Message, error) { return resp.Recv() }, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_ReplicationServiceRegisterGroup_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_ProtocolService_ReplicationServiceRegisterGroup_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_ReplicationServiceRegisterGroup_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_PeerList_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_ProtocolService_PeerList_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_PeerList_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_OutOfStoreReceive_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_ProtocolService_OutOfStoreReceive_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_OutOfStoreReceive_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_OutOfStoreSeal_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_ProtocolService_OutOfStoreSeal_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_OutOfStoreSeal_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ProtocolService_RefreshContactRequest_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_ProtocolService_RefreshContactRequest_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ProtocolService_RefreshContactRequest_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) return nil } var ( pattern_ProtocolService_ServiceExportData_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "ServiceExportData"}, "", runtime.AssumeColonVerbOpt(true))) pattern_ProtocolService_ServiceGetConfiguration_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "ServiceGetConfiguration"}, "", runtime.AssumeColonVerbOpt(true))) pattern_ProtocolService_ContactRequestReference_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "ContactRequestReference"}, "", runtime.AssumeColonVerbOpt(true))) pattern_ProtocolService_ContactRequestDisable_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "ContactRequestDisable"}, "", runtime.AssumeColonVerbOpt(true))) pattern_ProtocolService_ContactRequestEnable_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "ContactRequestEnable"}, "", runtime.AssumeColonVerbOpt(true))) pattern_ProtocolService_ContactRequestResetReference_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "ContactRequestResetReference"}, "", runtime.AssumeColonVerbOpt(true))) pattern_ProtocolService_ContactRequestSend_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "ContactRequestSend"}, "", runtime.AssumeColonVerbOpt(true))) pattern_ProtocolService_ContactRequestAccept_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "ContactRequestAccept"}, "", runtime.AssumeColonVerbOpt(true))) pattern_ProtocolService_ContactRequestDiscard_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "ContactRequestDiscard"}, "", runtime.AssumeColonVerbOpt(true))) pattern_ProtocolService_ShareContact_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "ShareContact"}, "", runtime.AssumeColonVerbOpt(true))) pattern_ProtocolService_DecodeContact_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "DecodeContact"}, "", runtime.AssumeColonVerbOpt(true))) pattern_ProtocolService_ContactBlock_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "ContactBlock"}, "", runtime.AssumeColonVerbOpt(true))) pattern_ProtocolService_ContactUnblock_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "ContactUnblock"}, "", runtime.AssumeColonVerbOpt(true))) pattern_ProtocolService_ContactAliasKeySend_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "ContactAliasKeySend"}, "", runtime.AssumeColonVerbOpt(true))) pattern_ProtocolService_MultiMemberGroupCreate_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "MultiMemberGroupCreate"}, "", runtime.AssumeColonVerbOpt(true))) pattern_ProtocolService_MultiMemberGroupJoin_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "MultiMemberGroupJoin"}, "", runtime.AssumeColonVerbOpt(true))) pattern_ProtocolService_MultiMemberGroupLeave_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "MultiMemberGroupLeave"}, "", runtime.AssumeColonVerbOpt(true))) pattern_ProtocolService_MultiMemberGroupAliasResolverDisclose_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "MultiMemberGroupAliasResolverDisclose"}, "", runtime.AssumeColonVerbOpt(true))) pattern_ProtocolService_MultiMemberGroupAdminRoleGrant_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "MultiMemberGroupAdminRoleGrant"}, "", runtime.AssumeColonVerbOpt(true))) pattern_ProtocolService_MultiMemberGroupInvitationCreate_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "MultiMemberGroupInvitationCreate"}, "", runtime.AssumeColonVerbOpt(true))) pattern_ProtocolService_AppMetadataSend_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "AppMetadataSend"}, "", runtime.AssumeColonVerbOpt(true))) pattern_ProtocolService_AppMessageSend_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "AppMessageSend"}, "", runtime.AssumeColonVerbOpt(true))) pattern_ProtocolService_GroupMetadataList_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "GroupMetadataList"}, "", runtime.AssumeColonVerbOpt(true))) pattern_ProtocolService_GroupMessageList_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "GroupMessageList"}, "", runtime.AssumeColonVerbOpt(true))) pattern_ProtocolService_GroupInfo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "GroupInfo"}, "", runtime.AssumeColonVerbOpt(true))) pattern_ProtocolService_ActivateGroup_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "ActivateGroup"}, "", runtime.AssumeColonVerbOpt(true))) pattern_ProtocolService_DeactivateGroup_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "DeactivateGroup"}, "", runtime.AssumeColonVerbOpt(true))) pattern_ProtocolService_GroupDeviceStatus_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "GroupDeviceStatus"}, "", runtime.AssumeColonVerbOpt(true))) pattern_ProtocolService_DebugListGroups_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "DebugListGroups"}, "", runtime.AssumeColonVerbOpt(true))) pattern_ProtocolService_DebugInspectGroupStore_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "DebugInspectGroupStore"}, "", runtime.AssumeColonVerbOpt(true))) pattern_ProtocolService_DebugGroup_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "DebugGroup"}, "", runtime.AssumeColonVerbOpt(true))) pattern_ProtocolService_SystemInfo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "SystemInfo"}, "", runtime.AssumeColonVerbOpt(true))) pattern_ProtocolService_CredentialVerificationServiceInitFlow_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "CredentialVerificationServiceInitFlow"}, "", runtime.AssumeColonVerbOpt(true))) pattern_ProtocolService_CredentialVerificationServiceCompleteFlow_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "CredentialVerificationServiceCompleteFlow"}, "", runtime.AssumeColonVerbOpt(true))) pattern_ProtocolService_VerifiedCredentialsList_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "VerifiedCredentialsList"}, "", runtime.AssumeColonVerbOpt(true))) pattern_ProtocolService_ReplicationServiceRegisterGroup_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "ReplicationServiceRegisterGroup"}, "", runtime.AssumeColonVerbOpt(true))) pattern_ProtocolService_PeerList_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "PeerList"}, "", runtime.AssumeColonVerbOpt(true))) pattern_ProtocolService_OutOfStoreReceive_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "OutOfStoreReceive"}, "", runtime.AssumeColonVerbOpt(true))) pattern_ProtocolService_OutOfStoreSeal_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "OutOfStoreSeal"}, "", runtime.AssumeColonVerbOpt(true))) pattern_ProtocolService_RefreshContactRequest_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "RefreshContactRequest"}, "", runtime.AssumeColonVerbOpt(true))) ) var ( forward_ProtocolService_ServiceExportData_0 = runtime.ForwardResponseStream forward_ProtocolService_ServiceGetConfiguration_0 = runtime.ForwardResponseMessage forward_ProtocolService_ContactRequestReference_0 = runtime.ForwardResponseMessage forward_ProtocolService_ContactRequestDisable_0 = runtime.ForwardResponseMessage forward_ProtocolService_ContactRequestEnable_0 = runtime.ForwardResponseMessage forward_ProtocolService_ContactRequestResetReference_0 = runtime.ForwardResponseMessage forward_ProtocolService_ContactRequestSend_0 = runtime.ForwardResponseMessage forward_ProtocolService_ContactRequestAccept_0 = runtime.ForwardResponseMessage forward_ProtocolService_ContactRequestDiscard_0 = runtime.ForwardResponseMessage forward_ProtocolService_ShareContact_0 = runtime.ForwardResponseMessage forward_ProtocolService_DecodeContact_0 = runtime.ForwardResponseMessage forward_ProtocolService_ContactBlock_0 = runtime.ForwardResponseMessage forward_ProtocolService_ContactUnblock_0 = runtime.ForwardResponseMessage forward_ProtocolService_ContactAliasKeySend_0 = runtime.ForwardResponseMessage forward_ProtocolService_MultiMemberGroupCreate_0 = runtime.ForwardResponseMessage forward_ProtocolService_MultiMemberGroupJoin_0 = runtime.ForwardResponseMessage forward_ProtocolService_MultiMemberGroupLeave_0 = runtime.ForwardResponseMessage forward_ProtocolService_MultiMemberGroupAliasResolverDisclose_0 = runtime.ForwardResponseMessage forward_ProtocolService_MultiMemberGroupAdminRoleGrant_0 = runtime.ForwardResponseMessage forward_ProtocolService_MultiMemberGroupInvitationCreate_0 = runtime.ForwardResponseMessage forward_ProtocolService_AppMetadataSend_0 = runtime.ForwardResponseMessage forward_ProtocolService_AppMessageSend_0 = runtime.ForwardResponseMessage forward_ProtocolService_GroupMetadataList_0 = runtime.ForwardResponseStream forward_ProtocolService_GroupMessageList_0 = runtime.ForwardResponseStream forward_ProtocolService_GroupInfo_0 = runtime.ForwardResponseMessage forward_ProtocolService_ActivateGroup_0 = runtime.ForwardResponseMessage forward_ProtocolService_DeactivateGroup_0 = runtime.ForwardResponseMessage forward_ProtocolService_GroupDeviceStatus_0 = runtime.ForwardResponseStream forward_ProtocolService_DebugListGroups_0 = runtime.ForwardResponseStream forward_ProtocolService_DebugInspectGroupStore_0 = runtime.ForwardResponseStream forward_ProtocolService_DebugGroup_0 = runtime.ForwardResponseMessage forward_ProtocolService_SystemInfo_0 = runtime.ForwardResponseMessage forward_ProtocolService_CredentialVerificationServiceInitFlow_0 = runtime.ForwardResponseMessage forward_ProtocolService_CredentialVerificationServiceCompleteFlow_0 = runtime.ForwardResponseMessage forward_ProtocolService_VerifiedCredentialsList_0 = runtime.ForwardResponseStream forward_ProtocolService_ReplicationServiceRegisterGroup_0 = runtime.ForwardResponseMessage forward_ProtocolService_PeerList_0 = runtime.ForwardResponseMessage forward_ProtocolService_OutOfStoreReceive_0 = runtime.ForwardResponseMessage forward_ProtocolService_OutOfStoreSeal_0 = runtime.ForwardResponseMessage forward_ProtocolService_RefreshContactRequest_0 = runtime.ForwardResponseMessage ) ================================================ FILE: pkg/protocoltypes/protocoltypes_grpc.pb.go ================================================ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.5.1 // - protoc (unknown) // source: protocoltypes.proto package protocoltypes import ( context "context" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" ) // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. // Requires gRPC-Go v1.64.0 or later. const _ = grpc.SupportPackageIsVersion9 const ( ProtocolService_ServiceExportData_FullMethodName = "/weshnet.protocol.v1.ProtocolService/ServiceExportData" ProtocolService_ServiceGetConfiguration_FullMethodName = "/weshnet.protocol.v1.ProtocolService/ServiceGetConfiguration" ProtocolService_ContactRequestReference_FullMethodName = "/weshnet.protocol.v1.ProtocolService/ContactRequestReference" ProtocolService_ContactRequestDisable_FullMethodName = "/weshnet.protocol.v1.ProtocolService/ContactRequestDisable" ProtocolService_ContactRequestEnable_FullMethodName = "/weshnet.protocol.v1.ProtocolService/ContactRequestEnable" ProtocolService_ContactRequestResetReference_FullMethodName = "/weshnet.protocol.v1.ProtocolService/ContactRequestResetReference" ProtocolService_ContactRequestSend_FullMethodName = "/weshnet.protocol.v1.ProtocolService/ContactRequestSend" ProtocolService_ContactRequestAccept_FullMethodName = "/weshnet.protocol.v1.ProtocolService/ContactRequestAccept" ProtocolService_ContactRequestDiscard_FullMethodName = "/weshnet.protocol.v1.ProtocolService/ContactRequestDiscard" ProtocolService_ShareContact_FullMethodName = "/weshnet.protocol.v1.ProtocolService/ShareContact" ProtocolService_DecodeContact_FullMethodName = "/weshnet.protocol.v1.ProtocolService/DecodeContact" ProtocolService_ContactBlock_FullMethodName = "/weshnet.protocol.v1.ProtocolService/ContactBlock" ProtocolService_ContactUnblock_FullMethodName = "/weshnet.protocol.v1.ProtocolService/ContactUnblock" ProtocolService_ContactAliasKeySend_FullMethodName = "/weshnet.protocol.v1.ProtocolService/ContactAliasKeySend" ProtocolService_MultiMemberGroupCreate_FullMethodName = "/weshnet.protocol.v1.ProtocolService/MultiMemberGroupCreate" ProtocolService_MultiMemberGroupJoin_FullMethodName = "/weshnet.protocol.v1.ProtocolService/MultiMemberGroupJoin" ProtocolService_MultiMemberGroupLeave_FullMethodName = "/weshnet.protocol.v1.ProtocolService/MultiMemberGroupLeave" ProtocolService_MultiMemberGroupAliasResolverDisclose_FullMethodName = "/weshnet.protocol.v1.ProtocolService/MultiMemberGroupAliasResolverDisclose" ProtocolService_MultiMemberGroupAdminRoleGrant_FullMethodName = "/weshnet.protocol.v1.ProtocolService/MultiMemberGroupAdminRoleGrant" ProtocolService_MultiMemberGroupInvitationCreate_FullMethodName = "/weshnet.protocol.v1.ProtocolService/MultiMemberGroupInvitationCreate" ProtocolService_AppMetadataSend_FullMethodName = "/weshnet.protocol.v1.ProtocolService/AppMetadataSend" ProtocolService_AppMessageSend_FullMethodName = "/weshnet.protocol.v1.ProtocolService/AppMessageSend" ProtocolService_GroupMetadataList_FullMethodName = "/weshnet.protocol.v1.ProtocolService/GroupMetadataList" ProtocolService_GroupMessageList_FullMethodName = "/weshnet.protocol.v1.ProtocolService/GroupMessageList" ProtocolService_GroupInfo_FullMethodName = "/weshnet.protocol.v1.ProtocolService/GroupInfo" ProtocolService_ActivateGroup_FullMethodName = "/weshnet.protocol.v1.ProtocolService/ActivateGroup" ProtocolService_DeactivateGroup_FullMethodName = "/weshnet.protocol.v1.ProtocolService/DeactivateGroup" ProtocolService_GroupDeviceStatus_FullMethodName = "/weshnet.protocol.v1.ProtocolService/GroupDeviceStatus" ProtocolService_DebugListGroups_FullMethodName = "/weshnet.protocol.v1.ProtocolService/DebugListGroups" ProtocolService_DebugInspectGroupStore_FullMethodName = "/weshnet.protocol.v1.ProtocolService/DebugInspectGroupStore" ProtocolService_DebugGroup_FullMethodName = "/weshnet.protocol.v1.ProtocolService/DebugGroup" ProtocolService_SystemInfo_FullMethodName = "/weshnet.protocol.v1.ProtocolService/SystemInfo" ProtocolService_CredentialVerificationServiceInitFlow_FullMethodName = "/weshnet.protocol.v1.ProtocolService/CredentialVerificationServiceInitFlow" ProtocolService_CredentialVerificationServiceCompleteFlow_FullMethodName = "/weshnet.protocol.v1.ProtocolService/CredentialVerificationServiceCompleteFlow" ProtocolService_VerifiedCredentialsList_FullMethodName = "/weshnet.protocol.v1.ProtocolService/VerifiedCredentialsList" ProtocolService_ReplicationServiceRegisterGroup_FullMethodName = "/weshnet.protocol.v1.ProtocolService/ReplicationServiceRegisterGroup" ProtocolService_PeerList_FullMethodName = "/weshnet.protocol.v1.ProtocolService/PeerList" ProtocolService_OutOfStoreReceive_FullMethodName = "/weshnet.protocol.v1.ProtocolService/OutOfStoreReceive" ProtocolService_OutOfStoreSeal_FullMethodName = "/weshnet.protocol.v1.ProtocolService/OutOfStoreSeal" ProtocolService_RefreshContactRequest_FullMethodName = "/weshnet.protocol.v1.ProtocolService/RefreshContactRequest" ) // ProtocolServiceClient is the client API for ProtocolService service. // // 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. // // ProtocolService is the top-level API to manage the Wesh protocol service. // Each active Wesh protocol service is considered as a Wesh device and is associated with a Wesh user. type ProtocolServiceClient interface { // ServiceExportData exports the current data of the protocol service ServiceExportData(ctx context.Context, in *ServiceExportData_Request, opts ...grpc.CallOption) (grpc.ServerStreamingClient[ServiceExportData_Reply], error) // ServiceGetConfiguration gets the current configuration of the protocol service ServiceGetConfiguration(ctx context.Context, in *ServiceGetConfiguration_Request, opts ...grpc.CallOption) (*ServiceGetConfiguration_Reply, error) // ContactRequestReference retrieves the information required to create a reference (ie. included in a shareable link) to the current account ContactRequestReference(ctx context.Context, in *ContactRequestReference_Request, opts ...grpc.CallOption) (*ContactRequestReference_Reply, error) // ContactRequestDisable disables incoming contact requests ContactRequestDisable(ctx context.Context, in *ContactRequestDisable_Request, opts ...grpc.CallOption) (*ContactRequestDisable_Reply, error) // ContactRequestEnable enables incoming contact requests ContactRequestEnable(ctx context.Context, in *ContactRequestEnable_Request, opts ...grpc.CallOption) (*ContactRequestEnable_Reply, error) // ContactRequestResetReference changes the contact request reference ContactRequestResetReference(ctx context.Context, in *ContactRequestResetReference_Request, opts ...grpc.CallOption) (*ContactRequestResetReference_Reply, error) // ContactRequestSend attempt to send a contact request ContactRequestSend(ctx context.Context, in *ContactRequestSend_Request, opts ...grpc.CallOption) (*ContactRequestSend_Reply, error) // ContactRequestAccept accepts a contact request ContactRequestAccept(ctx context.Context, in *ContactRequestAccept_Request, opts ...grpc.CallOption) (*ContactRequestAccept_Reply, error) // ContactRequestDiscard ignores a contact request, without informing the other user ContactRequestDiscard(ctx context.Context, in *ContactRequestDiscard_Request, opts ...grpc.CallOption) (*ContactRequestDiscard_Reply, error) // 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. ShareContact(ctx context.Context, in *ShareContact_Request, opts ...grpc.CallOption) (*ShareContact_Reply, error) // DecodeContact decodes the Protobuf encoding of a shareable contact which was returned by ShareContact. DecodeContact(ctx context.Context, in *DecodeContact_Request, opts ...grpc.CallOption) (*DecodeContact_Reply, error) // ContactBlock blocks a contact from sending requests ContactBlock(ctx context.Context, in *ContactBlock_Request, opts ...grpc.CallOption) (*ContactBlock_Reply, error) // ContactUnblock unblocks a contact from sending requests ContactUnblock(ctx context.Context, in *ContactUnblock_Request, opts ...grpc.CallOption) (*ContactUnblock_Reply, error) // 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 ContactAliasKeySend(ctx context.Context, in *ContactAliasKeySend_Request, opts ...grpc.CallOption) (*ContactAliasKeySend_Reply, error) // MultiMemberGroupCreate creates a new multi-member group MultiMemberGroupCreate(ctx context.Context, in *MultiMemberGroupCreate_Request, opts ...grpc.CallOption) (*MultiMemberGroupCreate_Reply, error) // MultiMemberGroupJoin joins a multi-member group MultiMemberGroupJoin(ctx context.Context, in *MultiMemberGroupJoin_Request, opts ...grpc.CallOption) (*MultiMemberGroupJoin_Reply, error) // MultiMemberGroupLeave leaves a multi-member group MultiMemberGroupLeave(ctx context.Context, in *MultiMemberGroupLeave_Request, opts ...grpc.CallOption) (*MultiMemberGroupLeave_Reply, error) // MultiMemberGroupAliasResolverDisclose discloses your alias resolver key MultiMemberGroupAliasResolverDisclose(ctx context.Context, in *MultiMemberGroupAliasResolverDisclose_Request, opts ...grpc.CallOption) (*MultiMemberGroupAliasResolverDisclose_Reply, error) // MultiMemberGroupAdminRoleGrant grants an admin role to a group member MultiMemberGroupAdminRoleGrant(ctx context.Context, in *MultiMemberGroupAdminRoleGrant_Request, opts ...grpc.CallOption) (*MultiMemberGroupAdminRoleGrant_Reply, error) // MultiMemberGroupInvitationCreate creates an invitation to a multi-member group MultiMemberGroupInvitationCreate(ctx context.Context, in *MultiMemberGroupInvitationCreate_Request, opts ...grpc.CallOption) (*MultiMemberGroupInvitationCreate_Reply, error) // AppMetadataSend adds an app event to the metadata store, the message is encrypted using a symmetric key and readable by future group members AppMetadataSend(ctx context.Context, in *AppMetadataSend_Request, opts ...grpc.CallOption) (*AppMetadataSend_Reply, error) // AppMessageSend adds an app event to the message store, the message is encrypted using a derived key and readable by current group members AppMessageSend(ctx context.Context, in *AppMessageSend_Request, opts ...grpc.CallOption) (*AppMessageSend_Reply, error) // GroupMetadataList replays previous and subscribes to new metadata events from the group GroupMetadataList(ctx context.Context, in *GroupMetadataList_Request, opts ...grpc.CallOption) (grpc.ServerStreamingClient[GroupMetadataEvent], error) // GroupMessageList replays previous and subscribes to new message events from the group GroupMessageList(ctx context.Context, in *GroupMessageList_Request, opts ...grpc.CallOption) (grpc.ServerStreamingClient[GroupMessageEvent], error) // GroupInfo retrieves information about a group GroupInfo(ctx context.Context, in *GroupInfo_Request, opts ...grpc.CallOption) (*GroupInfo_Reply, error) // ActivateGroup explicitly opens a group ActivateGroup(ctx context.Context, in *ActivateGroup_Request, opts ...grpc.CallOption) (*ActivateGroup_Reply, error) // DeactivateGroup closes a group DeactivateGroup(ctx context.Context, in *DeactivateGroup_Request, opts ...grpc.CallOption) (*DeactivateGroup_Reply, error) // GroupDeviceStatus monitor device status GroupDeviceStatus(ctx context.Context, in *GroupDeviceStatus_Request, opts ...grpc.CallOption) (grpc.ServerStreamingClient[GroupDeviceStatus_Reply], error) DebugListGroups(ctx context.Context, in *DebugListGroups_Request, opts ...grpc.CallOption) (grpc.ServerStreamingClient[DebugListGroups_Reply], error) DebugInspectGroupStore(ctx context.Context, in *DebugInspectGroupStore_Request, opts ...grpc.CallOption) (grpc.ServerStreamingClient[DebugInspectGroupStore_Reply], error) DebugGroup(ctx context.Context, in *DebugGroup_Request, opts ...grpc.CallOption) (*DebugGroup_Reply, error) SystemInfo(ctx context.Context, in *SystemInfo_Request, opts ...grpc.CallOption) (*SystemInfo_Reply, error) // CredentialVerificationServiceInitFlow Initialize a credential verification flow CredentialVerificationServiceInitFlow(ctx context.Context, in *CredentialVerificationServiceInitFlow_Request, opts ...grpc.CallOption) (*CredentialVerificationServiceInitFlow_Reply, error) // CredentialVerificationServiceCompleteFlow Completes a credential verification flow CredentialVerificationServiceCompleteFlow(ctx context.Context, in *CredentialVerificationServiceCompleteFlow_Request, opts ...grpc.CallOption) (*CredentialVerificationServiceCompleteFlow_Reply, error) // VerifiedCredentialsList Retrieves the list of verified credentials VerifiedCredentialsList(ctx context.Context, in *VerifiedCredentialsList_Request, opts ...grpc.CallOption) (grpc.ServerStreamingClient[VerifiedCredentialsList_Reply], error) // ReplicationServiceRegisterGroup Asks a replication service to distribute a group contents ReplicationServiceRegisterGroup(ctx context.Context, in *ReplicationServiceRegisterGroup_Request, opts ...grpc.CallOption) (*ReplicationServiceRegisterGroup_Reply, error) // PeerList returns a list of P2P peers PeerList(ctx context.Context, in *PeerList_Request, opts ...grpc.CallOption) (*PeerList_Reply, error) // OutOfStoreReceive parses a payload received outside a synchronized store OutOfStoreReceive(ctx context.Context, in *OutOfStoreReceive_Request, opts ...grpc.CallOption) (*OutOfStoreReceive_Reply, error) // OutOfStoreSeal creates a payload of a message present in store to be sent outside a synchronized store OutOfStoreSeal(ctx context.Context, in *OutOfStoreSeal_Request, opts ...grpc.CallOption) (*OutOfStoreSeal_Reply, error) // RefreshContactRequest try to refresh the contact request for the given contact RefreshContactRequest(ctx context.Context, in *RefreshContactRequest_Request, opts ...grpc.CallOption) (*RefreshContactRequest_Reply, error) } type protocolServiceClient struct { cc grpc.ClientConnInterface } func NewProtocolServiceClient(cc grpc.ClientConnInterface) ProtocolServiceClient { return &protocolServiceClient{cc} } func (c *protocolServiceClient) ServiceExportData(ctx context.Context, in *ServiceExportData_Request, opts ...grpc.CallOption) (grpc.ServerStreamingClient[ServiceExportData_Reply], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &ProtocolService_ServiceDesc.Streams[0], ProtocolService_ServiceExportData_FullMethodName, cOpts...) if err != nil { return nil, err } x := &grpc.GenericClientStream[ServiceExportData_Request, ServiceExportData_Reply]{ClientStream: stream} if err := x.ClientStream.SendMsg(in); err != nil { return nil, err } if err := x.ClientStream.CloseSend(); err != nil { return nil, err } return x, nil } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type ProtocolService_ServiceExportDataClient = grpc.ServerStreamingClient[ServiceExportData_Reply] func (c *protocolServiceClient) ServiceGetConfiguration(ctx context.Context, in *ServiceGetConfiguration_Request, opts ...grpc.CallOption) (*ServiceGetConfiguration_Reply, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ServiceGetConfiguration_Reply) err := c.cc.Invoke(ctx, ProtocolService_ServiceGetConfiguration_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *protocolServiceClient) ContactRequestReference(ctx context.Context, in *ContactRequestReference_Request, opts ...grpc.CallOption) (*ContactRequestReference_Reply, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ContactRequestReference_Reply) err := c.cc.Invoke(ctx, ProtocolService_ContactRequestReference_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *protocolServiceClient) ContactRequestDisable(ctx context.Context, in *ContactRequestDisable_Request, opts ...grpc.CallOption) (*ContactRequestDisable_Reply, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ContactRequestDisable_Reply) err := c.cc.Invoke(ctx, ProtocolService_ContactRequestDisable_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *protocolServiceClient) ContactRequestEnable(ctx context.Context, in *ContactRequestEnable_Request, opts ...grpc.CallOption) (*ContactRequestEnable_Reply, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ContactRequestEnable_Reply) err := c.cc.Invoke(ctx, ProtocolService_ContactRequestEnable_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *protocolServiceClient) ContactRequestResetReference(ctx context.Context, in *ContactRequestResetReference_Request, opts ...grpc.CallOption) (*ContactRequestResetReference_Reply, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ContactRequestResetReference_Reply) err := c.cc.Invoke(ctx, ProtocolService_ContactRequestResetReference_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *protocolServiceClient) ContactRequestSend(ctx context.Context, in *ContactRequestSend_Request, opts ...grpc.CallOption) (*ContactRequestSend_Reply, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ContactRequestSend_Reply) err := c.cc.Invoke(ctx, ProtocolService_ContactRequestSend_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *protocolServiceClient) ContactRequestAccept(ctx context.Context, in *ContactRequestAccept_Request, opts ...grpc.CallOption) (*ContactRequestAccept_Reply, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ContactRequestAccept_Reply) err := c.cc.Invoke(ctx, ProtocolService_ContactRequestAccept_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *protocolServiceClient) ContactRequestDiscard(ctx context.Context, in *ContactRequestDiscard_Request, opts ...grpc.CallOption) (*ContactRequestDiscard_Reply, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ContactRequestDiscard_Reply) err := c.cc.Invoke(ctx, ProtocolService_ContactRequestDiscard_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *protocolServiceClient) ShareContact(ctx context.Context, in *ShareContact_Request, opts ...grpc.CallOption) (*ShareContact_Reply, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ShareContact_Reply) err := c.cc.Invoke(ctx, ProtocolService_ShareContact_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *protocolServiceClient) DecodeContact(ctx context.Context, in *DecodeContact_Request, opts ...grpc.CallOption) (*DecodeContact_Reply, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(DecodeContact_Reply) err := c.cc.Invoke(ctx, ProtocolService_DecodeContact_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *protocolServiceClient) ContactBlock(ctx context.Context, in *ContactBlock_Request, opts ...grpc.CallOption) (*ContactBlock_Reply, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ContactBlock_Reply) err := c.cc.Invoke(ctx, ProtocolService_ContactBlock_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *protocolServiceClient) ContactUnblock(ctx context.Context, in *ContactUnblock_Request, opts ...grpc.CallOption) (*ContactUnblock_Reply, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ContactUnblock_Reply) err := c.cc.Invoke(ctx, ProtocolService_ContactUnblock_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *protocolServiceClient) ContactAliasKeySend(ctx context.Context, in *ContactAliasKeySend_Request, opts ...grpc.CallOption) (*ContactAliasKeySend_Reply, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ContactAliasKeySend_Reply) err := c.cc.Invoke(ctx, ProtocolService_ContactAliasKeySend_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *protocolServiceClient) MultiMemberGroupCreate(ctx context.Context, in *MultiMemberGroupCreate_Request, opts ...grpc.CallOption) (*MultiMemberGroupCreate_Reply, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(MultiMemberGroupCreate_Reply) err := c.cc.Invoke(ctx, ProtocolService_MultiMemberGroupCreate_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *protocolServiceClient) MultiMemberGroupJoin(ctx context.Context, in *MultiMemberGroupJoin_Request, opts ...grpc.CallOption) (*MultiMemberGroupJoin_Reply, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(MultiMemberGroupJoin_Reply) err := c.cc.Invoke(ctx, ProtocolService_MultiMemberGroupJoin_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *protocolServiceClient) MultiMemberGroupLeave(ctx context.Context, in *MultiMemberGroupLeave_Request, opts ...grpc.CallOption) (*MultiMemberGroupLeave_Reply, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(MultiMemberGroupLeave_Reply) err := c.cc.Invoke(ctx, ProtocolService_MultiMemberGroupLeave_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *protocolServiceClient) MultiMemberGroupAliasResolverDisclose(ctx context.Context, in *MultiMemberGroupAliasResolverDisclose_Request, opts ...grpc.CallOption) (*MultiMemberGroupAliasResolverDisclose_Reply, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(MultiMemberGroupAliasResolverDisclose_Reply) err := c.cc.Invoke(ctx, ProtocolService_MultiMemberGroupAliasResolverDisclose_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *protocolServiceClient) MultiMemberGroupAdminRoleGrant(ctx context.Context, in *MultiMemberGroupAdminRoleGrant_Request, opts ...grpc.CallOption) (*MultiMemberGroupAdminRoleGrant_Reply, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(MultiMemberGroupAdminRoleGrant_Reply) err := c.cc.Invoke(ctx, ProtocolService_MultiMemberGroupAdminRoleGrant_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *protocolServiceClient) MultiMemberGroupInvitationCreate(ctx context.Context, in *MultiMemberGroupInvitationCreate_Request, opts ...grpc.CallOption) (*MultiMemberGroupInvitationCreate_Reply, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(MultiMemberGroupInvitationCreate_Reply) err := c.cc.Invoke(ctx, ProtocolService_MultiMemberGroupInvitationCreate_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *protocolServiceClient) AppMetadataSend(ctx context.Context, in *AppMetadataSend_Request, opts ...grpc.CallOption) (*AppMetadataSend_Reply, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(AppMetadataSend_Reply) err := c.cc.Invoke(ctx, ProtocolService_AppMetadataSend_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *protocolServiceClient) AppMessageSend(ctx context.Context, in *AppMessageSend_Request, opts ...grpc.CallOption) (*AppMessageSend_Reply, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(AppMessageSend_Reply) err := c.cc.Invoke(ctx, ProtocolService_AppMessageSend_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *protocolServiceClient) GroupMetadataList(ctx context.Context, in *GroupMetadataList_Request, opts ...grpc.CallOption) (grpc.ServerStreamingClient[GroupMetadataEvent], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &ProtocolService_ServiceDesc.Streams[1], ProtocolService_GroupMetadataList_FullMethodName, cOpts...) if err != nil { return nil, err } x := &grpc.GenericClientStream[GroupMetadataList_Request, GroupMetadataEvent]{ClientStream: stream} if err := x.ClientStream.SendMsg(in); err != nil { return nil, err } if err := x.ClientStream.CloseSend(); err != nil { return nil, err } return x, nil } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type ProtocolService_GroupMetadataListClient = grpc.ServerStreamingClient[GroupMetadataEvent] func (c *protocolServiceClient) GroupMessageList(ctx context.Context, in *GroupMessageList_Request, opts ...grpc.CallOption) (grpc.ServerStreamingClient[GroupMessageEvent], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &ProtocolService_ServiceDesc.Streams[2], ProtocolService_GroupMessageList_FullMethodName, cOpts...) if err != nil { return nil, err } x := &grpc.GenericClientStream[GroupMessageList_Request, GroupMessageEvent]{ClientStream: stream} if err := x.ClientStream.SendMsg(in); err != nil { return nil, err } if err := x.ClientStream.CloseSend(); err != nil { return nil, err } return x, nil } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type ProtocolService_GroupMessageListClient = grpc.ServerStreamingClient[GroupMessageEvent] func (c *protocolServiceClient) GroupInfo(ctx context.Context, in *GroupInfo_Request, opts ...grpc.CallOption) (*GroupInfo_Reply, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GroupInfo_Reply) err := c.cc.Invoke(ctx, ProtocolService_GroupInfo_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *protocolServiceClient) ActivateGroup(ctx context.Context, in *ActivateGroup_Request, opts ...grpc.CallOption) (*ActivateGroup_Reply, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ActivateGroup_Reply) err := c.cc.Invoke(ctx, ProtocolService_ActivateGroup_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *protocolServiceClient) DeactivateGroup(ctx context.Context, in *DeactivateGroup_Request, opts ...grpc.CallOption) (*DeactivateGroup_Reply, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(DeactivateGroup_Reply) err := c.cc.Invoke(ctx, ProtocolService_DeactivateGroup_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *protocolServiceClient) GroupDeviceStatus(ctx context.Context, in *GroupDeviceStatus_Request, opts ...grpc.CallOption) (grpc.ServerStreamingClient[GroupDeviceStatus_Reply], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &ProtocolService_ServiceDesc.Streams[3], ProtocolService_GroupDeviceStatus_FullMethodName, cOpts...) if err != nil { return nil, err } x := &grpc.GenericClientStream[GroupDeviceStatus_Request, GroupDeviceStatus_Reply]{ClientStream: stream} if err := x.ClientStream.SendMsg(in); err != nil { return nil, err } if err := x.ClientStream.CloseSend(); err != nil { return nil, err } return x, nil } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type ProtocolService_GroupDeviceStatusClient = grpc.ServerStreamingClient[GroupDeviceStatus_Reply] func (c *protocolServiceClient) DebugListGroups(ctx context.Context, in *DebugListGroups_Request, opts ...grpc.CallOption) (grpc.ServerStreamingClient[DebugListGroups_Reply], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &ProtocolService_ServiceDesc.Streams[4], ProtocolService_DebugListGroups_FullMethodName, cOpts...) if err != nil { return nil, err } x := &grpc.GenericClientStream[DebugListGroups_Request, DebugListGroups_Reply]{ClientStream: stream} if err := x.ClientStream.SendMsg(in); err != nil { return nil, err } if err := x.ClientStream.CloseSend(); err != nil { return nil, err } return x, nil } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type ProtocolService_DebugListGroupsClient = grpc.ServerStreamingClient[DebugListGroups_Reply] func (c *protocolServiceClient) DebugInspectGroupStore(ctx context.Context, in *DebugInspectGroupStore_Request, opts ...grpc.CallOption) (grpc.ServerStreamingClient[DebugInspectGroupStore_Reply], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &ProtocolService_ServiceDesc.Streams[5], ProtocolService_DebugInspectGroupStore_FullMethodName, cOpts...) if err != nil { return nil, err } x := &grpc.GenericClientStream[DebugInspectGroupStore_Request, DebugInspectGroupStore_Reply]{ClientStream: stream} if err := x.ClientStream.SendMsg(in); err != nil { return nil, err } if err := x.ClientStream.CloseSend(); err != nil { return nil, err } return x, nil } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type ProtocolService_DebugInspectGroupStoreClient = grpc.ServerStreamingClient[DebugInspectGroupStore_Reply] func (c *protocolServiceClient) DebugGroup(ctx context.Context, in *DebugGroup_Request, opts ...grpc.CallOption) (*DebugGroup_Reply, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(DebugGroup_Reply) err := c.cc.Invoke(ctx, ProtocolService_DebugGroup_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *protocolServiceClient) SystemInfo(ctx context.Context, in *SystemInfo_Request, opts ...grpc.CallOption) (*SystemInfo_Reply, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(SystemInfo_Reply) err := c.cc.Invoke(ctx, ProtocolService_SystemInfo_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *protocolServiceClient) CredentialVerificationServiceInitFlow(ctx context.Context, in *CredentialVerificationServiceInitFlow_Request, opts ...grpc.CallOption) (*CredentialVerificationServiceInitFlow_Reply, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(CredentialVerificationServiceInitFlow_Reply) err := c.cc.Invoke(ctx, ProtocolService_CredentialVerificationServiceInitFlow_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *protocolServiceClient) CredentialVerificationServiceCompleteFlow(ctx context.Context, in *CredentialVerificationServiceCompleteFlow_Request, opts ...grpc.CallOption) (*CredentialVerificationServiceCompleteFlow_Reply, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(CredentialVerificationServiceCompleteFlow_Reply) err := c.cc.Invoke(ctx, ProtocolService_CredentialVerificationServiceCompleteFlow_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *protocolServiceClient) VerifiedCredentialsList(ctx context.Context, in *VerifiedCredentialsList_Request, opts ...grpc.CallOption) (grpc.ServerStreamingClient[VerifiedCredentialsList_Reply], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &ProtocolService_ServiceDesc.Streams[6], ProtocolService_VerifiedCredentialsList_FullMethodName, cOpts...) if err != nil { return nil, err } x := &grpc.GenericClientStream[VerifiedCredentialsList_Request, VerifiedCredentialsList_Reply]{ClientStream: stream} if err := x.ClientStream.SendMsg(in); err != nil { return nil, err } if err := x.ClientStream.CloseSend(); err != nil { return nil, err } return x, nil } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type ProtocolService_VerifiedCredentialsListClient = grpc.ServerStreamingClient[VerifiedCredentialsList_Reply] func (c *protocolServiceClient) ReplicationServiceRegisterGroup(ctx context.Context, in *ReplicationServiceRegisterGroup_Request, opts ...grpc.CallOption) (*ReplicationServiceRegisterGroup_Reply, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ReplicationServiceRegisterGroup_Reply) err := c.cc.Invoke(ctx, ProtocolService_ReplicationServiceRegisterGroup_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *protocolServiceClient) PeerList(ctx context.Context, in *PeerList_Request, opts ...grpc.CallOption) (*PeerList_Reply, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(PeerList_Reply) err := c.cc.Invoke(ctx, ProtocolService_PeerList_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *protocolServiceClient) OutOfStoreReceive(ctx context.Context, in *OutOfStoreReceive_Request, opts ...grpc.CallOption) (*OutOfStoreReceive_Reply, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(OutOfStoreReceive_Reply) err := c.cc.Invoke(ctx, ProtocolService_OutOfStoreReceive_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *protocolServiceClient) OutOfStoreSeal(ctx context.Context, in *OutOfStoreSeal_Request, opts ...grpc.CallOption) (*OutOfStoreSeal_Reply, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(OutOfStoreSeal_Reply) err := c.cc.Invoke(ctx, ProtocolService_OutOfStoreSeal_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *protocolServiceClient) RefreshContactRequest(ctx context.Context, in *RefreshContactRequest_Request, opts ...grpc.CallOption) (*RefreshContactRequest_Reply, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(RefreshContactRequest_Reply) err := c.cc.Invoke(ctx, ProtocolService_RefreshContactRequest_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } // ProtocolServiceServer is the server API for ProtocolService service. // All implementations must embed UnimplementedProtocolServiceServer // for forward compatibility. // // ProtocolService is the top-level API to manage the Wesh protocol service. // Each active Wesh protocol service is considered as a Wesh device and is associated with a Wesh user. type ProtocolServiceServer interface { // ServiceExportData exports the current data of the protocol service ServiceExportData(*ServiceExportData_Request, grpc.ServerStreamingServer[ServiceExportData_Reply]) error // ServiceGetConfiguration gets the current configuration of the protocol service ServiceGetConfiguration(context.Context, *ServiceGetConfiguration_Request) (*ServiceGetConfiguration_Reply, error) // ContactRequestReference retrieves the information required to create a reference (ie. included in a shareable link) to the current account ContactRequestReference(context.Context, *ContactRequestReference_Request) (*ContactRequestReference_Reply, error) // ContactRequestDisable disables incoming contact requests ContactRequestDisable(context.Context, *ContactRequestDisable_Request) (*ContactRequestDisable_Reply, error) // ContactRequestEnable enables incoming contact requests ContactRequestEnable(context.Context, *ContactRequestEnable_Request) (*ContactRequestEnable_Reply, error) // ContactRequestResetReference changes the contact request reference ContactRequestResetReference(context.Context, *ContactRequestResetReference_Request) (*ContactRequestResetReference_Reply, error) // ContactRequestSend attempt to send a contact request ContactRequestSend(context.Context, *ContactRequestSend_Request) (*ContactRequestSend_Reply, error) // ContactRequestAccept accepts a contact request ContactRequestAccept(context.Context, *ContactRequestAccept_Request) (*ContactRequestAccept_Reply, error) // ContactRequestDiscard ignores a contact request, without informing the other user ContactRequestDiscard(context.Context, *ContactRequestDiscard_Request) (*ContactRequestDiscard_Reply, error) // 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. ShareContact(context.Context, *ShareContact_Request) (*ShareContact_Reply, error) // DecodeContact decodes the Protobuf encoding of a shareable contact which was returned by ShareContact. DecodeContact(context.Context, *DecodeContact_Request) (*DecodeContact_Reply, error) // ContactBlock blocks a contact from sending requests ContactBlock(context.Context, *ContactBlock_Request) (*ContactBlock_Reply, error) // ContactUnblock unblocks a contact from sending requests ContactUnblock(context.Context, *ContactUnblock_Request) (*ContactUnblock_Reply, error) // 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 ContactAliasKeySend(context.Context, *ContactAliasKeySend_Request) (*ContactAliasKeySend_Reply, error) // MultiMemberGroupCreate creates a new multi-member group MultiMemberGroupCreate(context.Context, *MultiMemberGroupCreate_Request) (*MultiMemberGroupCreate_Reply, error) // MultiMemberGroupJoin joins a multi-member group MultiMemberGroupJoin(context.Context, *MultiMemberGroupJoin_Request) (*MultiMemberGroupJoin_Reply, error) // MultiMemberGroupLeave leaves a multi-member group MultiMemberGroupLeave(context.Context, *MultiMemberGroupLeave_Request) (*MultiMemberGroupLeave_Reply, error) // MultiMemberGroupAliasResolverDisclose discloses your alias resolver key MultiMemberGroupAliasResolverDisclose(context.Context, *MultiMemberGroupAliasResolverDisclose_Request) (*MultiMemberGroupAliasResolverDisclose_Reply, error) // MultiMemberGroupAdminRoleGrant grants an admin role to a group member MultiMemberGroupAdminRoleGrant(context.Context, *MultiMemberGroupAdminRoleGrant_Request) (*MultiMemberGroupAdminRoleGrant_Reply, error) // MultiMemberGroupInvitationCreate creates an invitation to a multi-member group MultiMemberGroupInvitationCreate(context.Context, *MultiMemberGroupInvitationCreate_Request) (*MultiMemberGroupInvitationCreate_Reply, error) // AppMetadataSend adds an app event to the metadata store, the message is encrypted using a symmetric key and readable by future group members AppMetadataSend(context.Context, *AppMetadataSend_Request) (*AppMetadataSend_Reply, error) // AppMessageSend adds an app event to the message store, the message is encrypted using a derived key and readable by current group members AppMessageSend(context.Context, *AppMessageSend_Request) (*AppMessageSend_Reply, error) // GroupMetadataList replays previous and subscribes to new metadata events from the group GroupMetadataList(*GroupMetadataList_Request, grpc.ServerStreamingServer[GroupMetadataEvent]) error // GroupMessageList replays previous and subscribes to new message events from the group GroupMessageList(*GroupMessageList_Request, grpc.ServerStreamingServer[GroupMessageEvent]) error // GroupInfo retrieves information about a group GroupInfo(context.Context, *GroupInfo_Request) (*GroupInfo_Reply, error) // ActivateGroup explicitly opens a group ActivateGroup(context.Context, *ActivateGroup_Request) (*ActivateGroup_Reply, error) // DeactivateGroup closes a group DeactivateGroup(context.Context, *DeactivateGroup_Request) (*DeactivateGroup_Reply, error) // GroupDeviceStatus monitor device status GroupDeviceStatus(*GroupDeviceStatus_Request, grpc.ServerStreamingServer[GroupDeviceStatus_Reply]) error DebugListGroups(*DebugListGroups_Request, grpc.ServerStreamingServer[DebugListGroups_Reply]) error DebugInspectGroupStore(*DebugInspectGroupStore_Request, grpc.ServerStreamingServer[DebugInspectGroupStore_Reply]) error DebugGroup(context.Context, *DebugGroup_Request) (*DebugGroup_Reply, error) SystemInfo(context.Context, *SystemInfo_Request) (*SystemInfo_Reply, error) // CredentialVerificationServiceInitFlow Initialize a credential verification flow CredentialVerificationServiceInitFlow(context.Context, *CredentialVerificationServiceInitFlow_Request) (*CredentialVerificationServiceInitFlow_Reply, error) // CredentialVerificationServiceCompleteFlow Completes a credential verification flow CredentialVerificationServiceCompleteFlow(context.Context, *CredentialVerificationServiceCompleteFlow_Request) (*CredentialVerificationServiceCompleteFlow_Reply, error) // VerifiedCredentialsList Retrieves the list of verified credentials VerifiedCredentialsList(*VerifiedCredentialsList_Request, grpc.ServerStreamingServer[VerifiedCredentialsList_Reply]) error // ReplicationServiceRegisterGroup Asks a replication service to distribute a group contents ReplicationServiceRegisterGroup(context.Context, *ReplicationServiceRegisterGroup_Request) (*ReplicationServiceRegisterGroup_Reply, error) // PeerList returns a list of P2P peers PeerList(context.Context, *PeerList_Request) (*PeerList_Reply, error) // OutOfStoreReceive parses a payload received outside a synchronized store OutOfStoreReceive(context.Context, *OutOfStoreReceive_Request) (*OutOfStoreReceive_Reply, error) // OutOfStoreSeal creates a payload of a message present in store to be sent outside a synchronized store OutOfStoreSeal(context.Context, *OutOfStoreSeal_Request) (*OutOfStoreSeal_Reply, error) // RefreshContactRequest try to refresh the contact request for the given contact RefreshContactRequest(context.Context, *RefreshContactRequest_Request) (*RefreshContactRequest_Reply, error) mustEmbedUnimplementedProtocolServiceServer() } // UnimplementedProtocolServiceServer must be embedded to have // forward compatible implementations. // // NOTE: this should be embedded by value instead of pointer to avoid a nil // pointer dereference when methods are called. type UnimplementedProtocolServiceServer struct{} func (UnimplementedProtocolServiceServer) ServiceExportData(*ServiceExportData_Request, grpc.ServerStreamingServer[ServiceExportData_Reply]) error { return status.Errorf(codes.Unimplemented, "method ServiceExportData not implemented") } func (UnimplementedProtocolServiceServer) ServiceGetConfiguration(context.Context, *ServiceGetConfiguration_Request) (*ServiceGetConfiguration_Reply, error) { return nil, status.Errorf(codes.Unimplemented, "method ServiceGetConfiguration not implemented") } func (UnimplementedProtocolServiceServer) ContactRequestReference(context.Context, *ContactRequestReference_Request) (*ContactRequestReference_Reply, error) { return nil, status.Errorf(codes.Unimplemented, "method ContactRequestReference not implemented") } func (UnimplementedProtocolServiceServer) ContactRequestDisable(context.Context, *ContactRequestDisable_Request) (*ContactRequestDisable_Reply, error) { return nil, status.Errorf(codes.Unimplemented, "method ContactRequestDisable not implemented") } func (UnimplementedProtocolServiceServer) ContactRequestEnable(context.Context, *ContactRequestEnable_Request) (*ContactRequestEnable_Reply, error) { return nil, status.Errorf(codes.Unimplemented, "method ContactRequestEnable not implemented") } func (UnimplementedProtocolServiceServer) ContactRequestResetReference(context.Context, *ContactRequestResetReference_Request) (*ContactRequestResetReference_Reply, error) { return nil, status.Errorf(codes.Unimplemented, "method ContactRequestResetReference not implemented") } func (UnimplementedProtocolServiceServer) ContactRequestSend(context.Context, *ContactRequestSend_Request) (*ContactRequestSend_Reply, error) { return nil, status.Errorf(codes.Unimplemented, "method ContactRequestSend not implemented") } func (UnimplementedProtocolServiceServer) ContactRequestAccept(context.Context, *ContactRequestAccept_Request) (*ContactRequestAccept_Reply, error) { return nil, status.Errorf(codes.Unimplemented, "method ContactRequestAccept not implemented") } func (UnimplementedProtocolServiceServer) ContactRequestDiscard(context.Context, *ContactRequestDiscard_Request) (*ContactRequestDiscard_Reply, error) { return nil, status.Errorf(codes.Unimplemented, "method ContactRequestDiscard not implemented") } func (UnimplementedProtocolServiceServer) ShareContact(context.Context, *ShareContact_Request) (*ShareContact_Reply, error) { return nil, status.Errorf(codes.Unimplemented, "method ShareContact not implemented") } func (UnimplementedProtocolServiceServer) DecodeContact(context.Context, *DecodeContact_Request) (*DecodeContact_Reply, error) { return nil, status.Errorf(codes.Unimplemented, "method DecodeContact not implemented") } func (UnimplementedProtocolServiceServer) ContactBlock(context.Context, *ContactBlock_Request) (*ContactBlock_Reply, error) { return nil, status.Errorf(codes.Unimplemented, "method ContactBlock not implemented") } func (UnimplementedProtocolServiceServer) ContactUnblock(context.Context, *ContactUnblock_Request) (*ContactUnblock_Reply, error) { return nil, status.Errorf(codes.Unimplemented, "method ContactUnblock not implemented") } func (UnimplementedProtocolServiceServer) ContactAliasKeySend(context.Context, *ContactAliasKeySend_Request) (*ContactAliasKeySend_Reply, error) { return nil, status.Errorf(codes.Unimplemented, "method ContactAliasKeySend not implemented") } func (UnimplementedProtocolServiceServer) MultiMemberGroupCreate(context.Context, *MultiMemberGroupCreate_Request) (*MultiMemberGroupCreate_Reply, error) { return nil, status.Errorf(codes.Unimplemented, "method MultiMemberGroupCreate not implemented") } func (UnimplementedProtocolServiceServer) MultiMemberGroupJoin(context.Context, *MultiMemberGroupJoin_Request) (*MultiMemberGroupJoin_Reply, error) { return nil, status.Errorf(codes.Unimplemented, "method MultiMemberGroupJoin not implemented") } func (UnimplementedProtocolServiceServer) MultiMemberGroupLeave(context.Context, *MultiMemberGroupLeave_Request) (*MultiMemberGroupLeave_Reply, error) { return nil, status.Errorf(codes.Unimplemented, "method MultiMemberGroupLeave not implemented") } func (UnimplementedProtocolServiceServer) MultiMemberGroupAliasResolverDisclose(context.Context, *MultiMemberGroupAliasResolverDisclose_Request) (*MultiMemberGroupAliasResolverDisclose_Reply, error) { return nil, status.Errorf(codes.Unimplemented, "method MultiMemberGroupAliasResolverDisclose not implemented") } func (UnimplementedProtocolServiceServer) MultiMemberGroupAdminRoleGrant(context.Context, *MultiMemberGroupAdminRoleGrant_Request) (*MultiMemberGroupAdminRoleGrant_Reply, error) { return nil, status.Errorf(codes.Unimplemented, "method MultiMemberGroupAdminRoleGrant not implemented") } func (UnimplementedProtocolServiceServer) MultiMemberGroupInvitationCreate(context.Context, *MultiMemberGroupInvitationCreate_Request) (*MultiMemberGroupInvitationCreate_Reply, error) { return nil, status.Errorf(codes.Unimplemented, "method MultiMemberGroupInvitationCreate not implemented") } func (UnimplementedProtocolServiceServer) AppMetadataSend(context.Context, *AppMetadataSend_Request) (*AppMetadataSend_Reply, error) { return nil, status.Errorf(codes.Unimplemented, "method AppMetadataSend not implemented") } func (UnimplementedProtocolServiceServer) AppMessageSend(context.Context, *AppMessageSend_Request) (*AppMessageSend_Reply, error) { return nil, status.Errorf(codes.Unimplemented, "method AppMessageSend not implemented") } func (UnimplementedProtocolServiceServer) GroupMetadataList(*GroupMetadataList_Request, grpc.ServerStreamingServer[GroupMetadataEvent]) error { return status.Errorf(codes.Unimplemented, "method GroupMetadataList not implemented") } func (UnimplementedProtocolServiceServer) GroupMessageList(*GroupMessageList_Request, grpc.ServerStreamingServer[GroupMessageEvent]) error { return status.Errorf(codes.Unimplemented, "method GroupMessageList not implemented") } func (UnimplementedProtocolServiceServer) GroupInfo(context.Context, *GroupInfo_Request) (*GroupInfo_Reply, error) { return nil, status.Errorf(codes.Unimplemented, "method GroupInfo not implemented") } func (UnimplementedProtocolServiceServer) ActivateGroup(context.Context, *ActivateGroup_Request) (*ActivateGroup_Reply, error) { return nil, status.Errorf(codes.Unimplemented, "method ActivateGroup not implemented") } func (UnimplementedProtocolServiceServer) DeactivateGroup(context.Context, *DeactivateGroup_Request) (*DeactivateGroup_Reply, error) { return nil, status.Errorf(codes.Unimplemented, "method DeactivateGroup not implemented") } func (UnimplementedProtocolServiceServer) GroupDeviceStatus(*GroupDeviceStatus_Request, grpc.ServerStreamingServer[GroupDeviceStatus_Reply]) error { return status.Errorf(codes.Unimplemented, "method GroupDeviceStatus not implemented") } func (UnimplementedProtocolServiceServer) DebugListGroups(*DebugListGroups_Request, grpc.ServerStreamingServer[DebugListGroups_Reply]) error { return status.Errorf(codes.Unimplemented, "method DebugListGroups not implemented") } func (UnimplementedProtocolServiceServer) DebugInspectGroupStore(*DebugInspectGroupStore_Request, grpc.ServerStreamingServer[DebugInspectGroupStore_Reply]) error { return status.Errorf(codes.Unimplemented, "method DebugInspectGroupStore not implemented") } func (UnimplementedProtocolServiceServer) DebugGroup(context.Context, *DebugGroup_Request) (*DebugGroup_Reply, error) { return nil, status.Errorf(codes.Unimplemented, "method DebugGroup not implemented") } func (UnimplementedProtocolServiceServer) SystemInfo(context.Context, *SystemInfo_Request) (*SystemInfo_Reply, error) { return nil, status.Errorf(codes.Unimplemented, "method SystemInfo not implemented") } func (UnimplementedProtocolServiceServer) CredentialVerificationServiceInitFlow(context.Context, *CredentialVerificationServiceInitFlow_Request) (*CredentialVerificationServiceInitFlow_Reply, error) { return nil, status.Errorf(codes.Unimplemented, "method CredentialVerificationServiceInitFlow not implemented") } func (UnimplementedProtocolServiceServer) CredentialVerificationServiceCompleteFlow(context.Context, *CredentialVerificationServiceCompleteFlow_Request) (*CredentialVerificationServiceCompleteFlow_Reply, error) { return nil, status.Errorf(codes.Unimplemented, "method CredentialVerificationServiceCompleteFlow not implemented") } func (UnimplementedProtocolServiceServer) VerifiedCredentialsList(*VerifiedCredentialsList_Request, grpc.ServerStreamingServer[VerifiedCredentialsList_Reply]) error { return status.Errorf(codes.Unimplemented, "method VerifiedCredentialsList not implemented") } func (UnimplementedProtocolServiceServer) ReplicationServiceRegisterGroup(context.Context, *ReplicationServiceRegisterGroup_Request) (*ReplicationServiceRegisterGroup_Reply, error) { return nil, status.Errorf(codes.Unimplemented, "method ReplicationServiceRegisterGroup not implemented") } func (UnimplementedProtocolServiceServer) PeerList(context.Context, *PeerList_Request) (*PeerList_Reply, error) { return nil, status.Errorf(codes.Unimplemented, "method PeerList not implemented") } func (UnimplementedProtocolServiceServer) OutOfStoreReceive(context.Context, *OutOfStoreReceive_Request) (*OutOfStoreReceive_Reply, error) { return nil, status.Errorf(codes.Unimplemented, "method OutOfStoreReceive not implemented") } func (UnimplementedProtocolServiceServer) OutOfStoreSeal(context.Context, *OutOfStoreSeal_Request) (*OutOfStoreSeal_Reply, error) { return nil, status.Errorf(codes.Unimplemented, "method OutOfStoreSeal not implemented") } func (UnimplementedProtocolServiceServer) RefreshContactRequest(context.Context, *RefreshContactRequest_Request) (*RefreshContactRequest_Reply, error) { return nil, status.Errorf(codes.Unimplemented, "method RefreshContactRequest not implemented") } func (UnimplementedProtocolServiceServer) mustEmbedUnimplementedProtocolServiceServer() {} func (UnimplementedProtocolServiceServer) testEmbeddedByValue() {} // UnsafeProtocolServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to ProtocolServiceServer will // result in compilation errors. type UnsafeProtocolServiceServer interface { mustEmbedUnimplementedProtocolServiceServer() } func RegisterProtocolServiceServer(s grpc.ServiceRegistrar, srv ProtocolServiceServer) { // If the following call pancis, it indicates UnimplementedProtocolServiceServer was // embedded by pointer and is nil. This will cause panics if an // unimplemented method is ever invoked, so we test this at initialization // time to prevent it from happening at runtime later due to I/O. if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { t.testEmbeddedByValue() } s.RegisterService(&ProtocolService_ServiceDesc, srv) } func _ProtocolService_ServiceExportData_Handler(srv interface{}, stream grpc.ServerStream) error { m := new(ServiceExportData_Request) if err := stream.RecvMsg(m); err != nil { return err } return srv.(ProtocolServiceServer).ServiceExportData(m, &grpc.GenericServerStream[ServiceExportData_Request, ServiceExportData_Reply]{ServerStream: stream}) } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type ProtocolService_ServiceExportDataServer = grpc.ServerStreamingServer[ServiceExportData_Reply] func _ProtocolService_ServiceGetConfiguration_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ServiceGetConfiguration_Request) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(ProtocolServiceServer).ServiceGetConfiguration(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: ProtocolService_ServiceGetConfiguration_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ProtocolServiceServer).ServiceGetConfiguration(ctx, req.(*ServiceGetConfiguration_Request)) } return interceptor(ctx, in, info, handler) } func _ProtocolService_ContactRequestReference_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ContactRequestReference_Request) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(ProtocolServiceServer).ContactRequestReference(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: ProtocolService_ContactRequestReference_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ProtocolServiceServer).ContactRequestReference(ctx, req.(*ContactRequestReference_Request)) } return interceptor(ctx, in, info, handler) } func _ProtocolService_ContactRequestDisable_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ContactRequestDisable_Request) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(ProtocolServiceServer).ContactRequestDisable(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: ProtocolService_ContactRequestDisable_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ProtocolServiceServer).ContactRequestDisable(ctx, req.(*ContactRequestDisable_Request)) } return interceptor(ctx, in, info, handler) } func _ProtocolService_ContactRequestEnable_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ContactRequestEnable_Request) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(ProtocolServiceServer).ContactRequestEnable(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: ProtocolService_ContactRequestEnable_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ProtocolServiceServer).ContactRequestEnable(ctx, req.(*ContactRequestEnable_Request)) } return interceptor(ctx, in, info, handler) } func _ProtocolService_ContactRequestResetReference_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ContactRequestResetReference_Request) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(ProtocolServiceServer).ContactRequestResetReference(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: ProtocolService_ContactRequestResetReference_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ProtocolServiceServer).ContactRequestResetReference(ctx, req.(*ContactRequestResetReference_Request)) } return interceptor(ctx, in, info, handler) } func _ProtocolService_ContactRequestSend_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ContactRequestSend_Request) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(ProtocolServiceServer).ContactRequestSend(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: ProtocolService_ContactRequestSend_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ProtocolServiceServer).ContactRequestSend(ctx, req.(*ContactRequestSend_Request)) } return interceptor(ctx, in, info, handler) } func _ProtocolService_ContactRequestAccept_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ContactRequestAccept_Request) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(ProtocolServiceServer).ContactRequestAccept(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: ProtocolService_ContactRequestAccept_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ProtocolServiceServer).ContactRequestAccept(ctx, req.(*ContactRequestAccept_Request)) } return interceptor(ctx, in, info, handler) } func _ProtocolService_ContactRequestDiscard_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ContactRequestDiscard_Request) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(ProtocolServiceServer).ContactRequestDiscard(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: ProtocolService_ContactRequestDiscard_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ProtocolServiceServer).ContactRequestDiscard(ctx, req.(*ContactRequestDiscard_Request)) } return interceptor(ctx, in, info, handler) } func _ProtocolService_ShareContact_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ShareContact_Request) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(ProtocolServiceServer).ShareContact(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: ProtocolService_ShareContact_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ProtocolServiceServer).ShareContact(ctx, req.(*ShareContact_Request)) } return interceptor(ctx, in, info, handler) } func _ProtocolService_DecodeContact_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(DecodeContact_Request) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(ProtocolServiceServer).DecodeContact(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: ProtocolService_DecodeContact_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ProtocolServiceServer).DecodeContact(ctx, req.(*DecodeContact_Request)) } return interceptor(ctx, in, info, handler) } func _ProtocolService_ContactBlock_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ContactBlock_Request) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(ProtocolServiceServer).ContactBlock(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: ProtocolService_ContactBlock_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ProtocolServiceServer).ContactBlock(ctx, req.(*ContactBlock_Request)) } return interceptor(ctx, in, info, handler) } func _ProtocolService_ContactUnblock_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ContactUnblock_Request) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(ProtocolServiceServer).ContactUnblock(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: ProtocolService_ContactUnblock_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ProtocolServiceServer).ContactUnblock(ctx, req.(*ContactUnblock_Request)) } return interceptor(ctx, in, info, handler) } func _ProtocolService_ContactAliasKeySend_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ContactAliasKeySend_Request) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(ProtocolServiceServer).ContactAliasKeySend(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: ProtocolService_ContactAliasKeySend_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ProtocolServiceServer).ContactAliasKeySend(ctx, req.(*ContactAliasKeySend_Request)) } return interceptor(ctx, in, info, handler) } func _ProtocolService_MultiMemberGroupCreate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(MultiMemberGroupCreate_Request) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(ProtocolServiceServer).MultiMemberGroupCreate(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: ProtocolService_MultiMemberGroupCreate_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ProtocolServiceServer).MultiMemberGroupCreate(ctx, req.(*MultiMemberGroupCreate_Request)) } return interceptor(ctx, in, info, handler) } func _ProtocolService_MultiMemberGroupJoin_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(MultiMemberGroupJoin_Request) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(ProtocolServiceServer).MultiMemberGroupJoin(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: ProtocolService_MultiMemberGroupJoin_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ProtocolServiceServer).MultiMemberGroupJoin(ctx, req.(*MultiMemberGroupJoin_Request)) } return interceptor(ctx, in, info, handler) } func _ProtocolService_MultiMemberGroupLeave_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(MultiMemberGroupLeave_Request) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(ProtocolServiceServer).MultiMemberGroupLeave(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: ProtocolService_MultiMemberGroupLeave_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ProtocolServiceServer).MultiMemberGroupLeave(ctx, req.(*MultiMemberGroupLeave_Request)) } return interceptor(ctx, in, info, handler) } func _ProtocolService_MultiMemberGroupAliasResolverDisclose_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(MultiMemberGroupAliasResolverDisclose_Request) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(ProtocolServiceServer).MultiMemberGroupAliasResolverDisclose(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: ProtocolService_MultiMemberGroupAliasResolverDisclose_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ProtocolServiceServer).MultiMemberGroupAliasResolverDisclose(ctx, req.(*MultiMemberGroupAliasResolverDisclose_Request)) } return interceptor(ctx, in, info, handler) } func _ProtocolService_MultiMemberGroupAdminRoleGrant_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(MultiMemberGroupAdminRoleGrant_Request) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(ProtocolServiceServer).MultiMemberGroupAdminRoleGrant(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: ProtocolService_MultiMemberGroupAdminRoleGrant_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ProtocolServiceServer).MultiMemberGroupAdminRoleGrant(ctx, req.(*MultiMemberGroupAdminRoleGrant_Request)) } return interceptor(ctx, in, info, handler) } func _ProtocolService_MultiMemberGroupInvitationCreate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(MultiMemberGroupInvitationCreate_Request) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(ProtocolServiceServer).MultiMemberGroupInvitationCreate(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: ProtocolService_MultiMemberGroupInvitationCreate_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ProtocolServiceServer).MultiMemberGroupInvitationCreate(ctx, req.(*MultiMemberGroupInvitationCreate_Request)) } return interceptor(ctx, in, info, handler) } func _ProtocolService_AppMetadataSend_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(AppMetadataSend_Request) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(ProtocolServiceServer).AppMetadataSend(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: ProtocolService_AppMetadataSend_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ProtocolServiceServer).AppMetadataSend(ctx, req.(*AppMetadataSend_Request)) } return interceptor(ctx, in, info, handler) } func _ProtocolService_AppMessageSend_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(AppMessageSend_Request) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(ProtocolServiceServer).AppMessageSend(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: ProtocolService_AppMessageSend_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ProtocolServiceServer).AppMessageSend(ctx, req.(*AppMessageSend_Request)) } return interceptor(ctx, in, info, handler) } func _ProtocolService_GroupMetadataList_Handler(srv interface{}, stream grpc.ServerStream) error { m := new(GroupMetadataList_Request) if err := stream.RecvMsg(m); err != nil { return err } return srv.(ProtocolServiceServer).GroupMetadataList(m, &grpc.GenericServerStream[GroupMetadataList_Request, GroupMetadataEvent]{ServerStream: stream}) } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type ProtocolService_GroupMetadataListServer = grpc.ServerStreamingServer[GroupMetadataEvent] func _ProtocolService_GroupMessageList_Handler(srv interface{}, stream grpc.ServerStream) error { m := new(GroupMessageList_Request) if err := stream.RecvMsg(m); err != nil { return err } return srv.(ProtocolServiceServer).GroupMessageList(m, &grpc.GenericServerStream[GroupMessageList_Request, GroupMessageEvent]{ServerStream: stream}) } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type ProtocolService_GroupMessageListServer = grpc.ServerStreamingServer[GroupMessageEvent] func _ProtocolService_GroupInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(GroupInfo_Request) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(ProtocolServiceServer).GroupInfo(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: ProtocolService_GroupInfo_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ProtocolServiceServer).GroupInfo(ctx, req.(*GroupInfo_Request)) } return interceptor(ctx, in, info, handler) } func _ProtocolService_ActivateGroup_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ActivateGroup_Request) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(ProtocolServiceServer).ActivateGroup(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: ProtocolService_ActivateGroup_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ProtocolServiceServer).ActivateGroup(ctx, req.(*ActivateGroup_Request)) } return interceptor(ctx, in, info, handler) } func _ProtocolService_DeactivateGroup_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(DeactivateGroup_Request) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(ProtocolServiceServer).DeactivateGroup(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: ProtocolService_DeactivateGroup_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ProtocolServiceServer).DeactivateGroup(ctx, req.(*DeactivateGroup_Request)) } return interceptor(ctx, in, info, handler) } func _ProtocolService_GroupDeviceStatus_Handler(srv interface{}, stream grpc.ServerStream) error { m := new(GroupDeviceStatus_Request) if err := stream.RecvMsg(m); err != nil { return err } return srv.(ProtocolServiceServer).GroupDeviceStatus(m, &grpc.GenericServerStream[GroupDeviceStatus_Request, GroupDeviceStatus_Reply]{ServerStream: stream}) } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type ProtocolService_GroupDeviceStatusServer = grpc.ServerStreamingServer[GroupDeviceStatus_Reply] func _ProtocolService_DebugListGroups_Handler(srv interface{}, stream grpc.ServerStream) error { m := new(DebugListGroups_Request) if err := stream.RecvMsg(m); err != nil { return err } return srv.(ProtocolServiceServer).DebugListGroups(m, &grpc.GenericServerStream[DebugListGroups_Request, DebugListGroups_Reply]{ServerStream: stream}) } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type ProtocolService_DebugListGroupsServer = grpc.ServerStreamingServer[DebugListGroups_Reply] func _ProtocolService_DebugInspectGroupStore_Handler(srv interface{}, stream grpc.ServerStream) error { m := new(DebugInspectGroupStore_Request) if err := stream.RecvMsg(m); err != nil { return err } return srv.(ProtocolServiceServer).DebugInspectGroupStore(m, &grpc.GenericServerStream[DebugInspectGroupStore_Request, DebugInspectGroupStore_Reply]{ServerStream: stream}) } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type ProtocolService_DebugInspectGroupStoreServer = grpc.ServerStreamingServer[DebugInspectGroupStore_Reply] func _ProtocolService_DebugGroup_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(DebugGroup_Request) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(ProtocolServiceServer).DebugGroup(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: ProtocolService_DebugGroup_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ProtocolServiceServer).DebugGroup(ctx, req.(*DebugGroup_Request)) } return interceptor(ctx, in, info, handler) } func _ProtocolService_SystemInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(SystemInfo_Request) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(ProtocolServiceServer).SystemInfo(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: ProtocolService_SystemInfo_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ProtocolServiceServer).SystemInfo(ctx, req.(*SystemInfo_Request)) } return interceptor(ctx, in, info, handler) } func _ProtocolService_CredentialVerificationServiceInitFlow_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(CredentialVerificationServiceInitFlow_Request) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(ProtocolServiceServer).CredentialVerificationServiceInitFlow(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: ProtocolService_CredentialVerificationServiceInitFlow_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ProtocolServiceServer).CredentialVerificationServiceInitFlow(ctx, req.(*CredentialVerificationServiceInitFlow_Request)) } return interceptor(ctx, in, info, handler) } func _ProtocolService_CredentialVerificationServiceCompleteFlow_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(CredentialVerificationServiceCompleteFlow_Request) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(ProtocolServiceServer).CredentialVerificationServiceCompleteFlow(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: ProtocolService_CredentialVerificationServiceCompleteFlow_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ProtocolServiceServer).CredentialVerificationServiceCompleteFlow(ctx, req.(*CredentialVerificationServiceCompleteFlow_Request)) } return interceptor(ctx, in, info, handler) } func _ProtocolService_VerifiedCredentialsList_Handler(srv interface{}, stream grpc.ServerStream) error { m := new(VerifiedCredentialsList_Request) if err := stream.RecvMsg(m); err != nil { return err } return srv.(ProtocolServiceServer).VerifiedCredentialsList(m, &grpc.GenericServerStream[VerifiedCredentialsList_Request, VerifiedCredentialsList_Reply]{ServerStream: stream}) } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type ProtocolService_VerifiedCredentialsListServer = grpc.ServerStreamingServer[VerifiedCredentialsList_Reply] func _ProtocolService_ReplicationServiceRegisterGroup_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ReplicationServiceRegisterGroup_Request) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(ProtocolServiceServer).ReplicationServiceRegisterGroup(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: ProtocolService_ReplicationServiceRegisterGroup_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ProtocolServiceServer).ReplicationServiceRegisterGroup(ctx, req.(*ReplicationServiceRegisterGroup_Request)) } return interceptor(ctx, in, info, handler) } func _ProtocolService_PeerList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(PeerList_Request) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(ProtocolServiceServer).PeerList(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: ProtocolService_PeerList_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ProtocolServiceServer).PeerList(ctx, req.(*PeerList_Request)) } return interceptor(ctx, in, info, handler) } func _ProtocolService_OutOfStoreReceive_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(OutOfStoreReceive_Request) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(ProtocolServiceServer).OutOfStoreReceive(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: ProtocolService_OutOfStoreReceive_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ProtocolServiceServer).OutOfStoreReceive(ctx, req.(*OutOfStoreReceive_Request)) } return interceptor(ctx, in, info, handler) } func _ProtocolService_OutOfStoreSeal_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(OutOfStoreSeal_Request) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(ProtocolServiceServer).OutOfStoreSeal(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: ProtocolService_OutOfStoreSeal_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ProtocolServiceServer).OutOfStoreSeal(ctx, req.(*OutOfStoreSeal_Request)) } return interceptor(ctx, in, info, handler) } func _ProtocolService_RefreshContactRequest_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(RefreshContactRequest_Request) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(ProtocolServiceServer).RefreshContactRequest(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: ProtocolService_RefreshContactRequest_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ProtocolServiceServer).RefreshContactRequest(ctx, req.(*RefreshContactRequest_Request)) } return interceptor(ctx, in, info, handler) } // ProtocolService_ServiceDesc is the grpc.ServiceDesc for ProtocolService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var ProtocolService_ServiceDesc = grpc.ServiceDesc{ ServiceName: "weshnet.protocol.v1.ProtocolService", HandlerType: (*ProtocolServiceServer)(nil), Methods: []grpc.MethodDesc{ { MethodName: "ServiceGetConfiguration", Handler: _ProtocolService_ServiceGetConfiguration_Handler, }, { MethodName: "ContactRequestReference", Handler: _ProtocolService_ContactRequestReference_Handler, }, { MethodName: "ContactRequestDisable", Handler: _ProtocolService_ContactRequestDisable_Handler, }, { MethodName: "ContactRequestEnable", Handler: _ProtocolService_ContactRequestEnable_Handler, }, { MethodName: "ContactRequestResetReference", Handler: _ProtocolService_ContactRequestResetReference_Handler, }, { MethodName: "ContactRequestSend", Handler: _ProtocolService_ContactRequestSend_Handler, }, { MethodName: "ContactRequestAccept", Handler: _ProtocolService_ContactRequestAccept_Handler, }, { MethodName: "ContactRequestDiscard", Handler: _ProtocolService_ContactRequestDiscard_Handler, }, { MethodName: "ShareContact", Handler: _ProtocolService_ShareContact_Handler, }, { MethodName: "DecodeContact", Handler: _ProtocolService_DecodeContact_Handler, }, { MethodName: "ContactBlock", Handler: _ProtocolService_ContactBlock_Handler, }, { MethodName: "ContactUnblock", Handler: _ProtocolService_ContactUnblock_Handler, }, { MethodName: "ContactAliasKeySend", Handler: _ProtocolService_ContactAliasKeySend_Handler, }, { MethodName: "MultiMemberGroupCreate", Handler: _ProtocolService_MultiMemberGroupCreate_Handler, }, { MethodName: "MultiMemberGroupJoin", Handler: _ProtocolService_MultiMemberGroupJoin_Handler, }, { MethodName: "MultiMemberGroupLeave", Handler: _ProtocolService_MultiMemberGroupLeave_Handler, }, { MethodName: "MultiMemberGroupAliasResolverDisclose", Handler: _ProtocolService_MultiMemberGroupAliasResolverDisclose_Handler, }, { MethodName: "MultiMemberGroupAdminRoleGrant", Handler: _ProtocolService_MultiMemberGroupAdminRoleGrant_Handler, }, { MethodName: "MultiMemberGroupInvitationCreate", Handler: _ProtocolService_MultiMemberGroupInvitationCreate_Handler, }, { MethodName: "AppMetadataSend", Handler: _ProtocolService_AppMetadataSend_Handler, }, { MethodName: "AppMessageSend", Handler: _ProtocolService_AppMessageSend_Handler, }, { MethodName: "GroupInfo", Handler: _ProtocolService_GroupInfo_Handler, }, { MethodName: "ActivateGroup", Handler: _ProtocolService_ActivateGroup_Handler, }, { MethodName: "DeactivateGroup", Handler: _ProtocolService_DeactivateGroup_Handler, }, { MethodName: "DebugGroup", Handler: _ProtocolService_DebugGroup_Handler, }, { MethodName: "SystemInfo", Handler: _ProtocolService_SystemInfo_Handler, }, { MethodName: "CredentialVerificationServiceInitFlow", Handler: _ProtocolService_CredentialVerificationServiceInitFlow_Handler, }, { MethodName: "CredentialVerificationServiceCompleteFlow", Handler: _ProtocolService_CredentialVerificationServiceCompleteFlow_Handler, }, { MethodName: "ReplicationServiceRegisterGroup", Handler: _ProtocolService_ReplicationServiceRegisterGroup_Handler, }, { MethodName: "PeerList", Handler: _ProtocolService_PeerList_Handler, }, { MethodName: "OutOfStoreReceive", Handler: _ProtocolService_OutOfStoreReceive_Handler, }, { MethodName: "OutOfStoreSeal", Handler: _ProtocolService_OutOfStoreSeal_Handler, }, { MethodName: "RefreshContactRequest", Handler: _ProtocolService_RefreshContactRequest_Handler, }, }, Streams: []grpc.StreamDesc{ { StreamName: "ServiceExportData", Handler: _ProtocolService_ServiceExportData_Handler, ServerStreams: true, }, { StreamName: "GroupMetadataList", Handler: _ProtocolService_GroupMetadataList_Handler, ServerStreams: true, }, { StreamName: "GroupMessageList", Handler: _ProtocolService_GroupMessageList_Handler, ServerStreams: true, }, { StreamName: "GroupDeviceStatus", Handler: _ProtocolService_GroupDeviceStatus_Handler, ServerStreams: true, }, { StreamName: "DebugListGroups", Handler: _ProtocolService_DebugListGroups_Handler, ServerStreams: true, }, { StreamName: "DebugInspectGroupStore", Handler: _ProtocolService_DebugInspectGroupStore_Handler, ServerStreams: true, }, { StreamName: "VerifiedCredentialsList", Handler: _ProtocolService_VerifiedCredentialsList_Handler, ServerStreams: true, }, }, Metadata: "protocoltypes.proto", } ================================================ FILE: pkg/protoio/full.go ================================================ // Protocol Buffers for Go with Gadgets // // Copyright (c) 2013, The GoGo Authors. All rights reserved. // http://github.com/gogo/protobuf // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package protoio import ( "io" "google.golang.org/protobuf/proto" ) func NewFullWriter(w io.Writer) WriteCloser { return &fullWriter{w, nil} } type fullWriter struct { w io.Writer buffer []byte } func (writer *fullWriter) WriteMsg(msg proto.Message) (err error) { var data []byte if m, ok := msg.(marshaler); ok { n, ok := getSize(m) if !ok { _, err = proto.Marshal(msg) if err != nil { return err } } if n >= len(writer.buffer) { writer.buffer = make([]byte, n) } _, err = m.MarshalTo(writer.buffer) if err != nil { return err } data = writer.buffer[:n] } else { data, err = proto.Marshal(msg) if err != nil { return err } } _, err = writer.w.Write(data) return err } func (writer *fullWriter) Close() error { if closer, ok := writer.w.(io.Closer); ok { return closer.Close() } return nil } type fullReader struct { r io.Reader buf []byte } func NewFullReader(r io.Reader, maxSize int) ReadCloser { return &fullReader{r, make([]byte, maxSize)} } func (reader *fullReader) ReadMsg(msg proto.Message) error { length, err := reader.r.Read(reader.buf) if err != nil { return err } return proto.Unmarshal(reader.buf[:length], msg) } func (reader *fullReader) Close() error { if closer, ok := reader.r.(io.Closer); ok { return closer.Close() } return nil } ================================================ FILE: pkg/protoio/io.go ================================================ // Protocol Buffers for Go with Gadgets // // Copyright (c) 2013, The GoGo Authors. All rights reserved. // http://github.com/gogo/protobuf // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package protoio import ( "io" "google.golang.org/protobuf/proto" ) type Writer interface { WriteMsg(proto.Message) error } type WriteCloser interface { Writer io.Closer } type Reader interface { ReadMsg(msg proto.Message) error } type ReadCloser interface { Reader io.Closer } type marshaler interface { MarshalTo(data []byte) (n int, err error) } func getSize(v any) (int, bool) { if sz, ok := v.(interface { Size() (n int) }); ok { return sz.Size(), true } else if sz, ok := v.(interface { ProtoSize() (n int) }); ok { return sz.ProtoSize(), true } return 0, false } ================================================ FILE: pkg/protoio/uint32.go ================================================ // Protocol Buffers for Go with Gadgets // // Copyright (c) 2013, The GoGo Authors. All rights reserved. // http://github.com/gogo/protobuf // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package protoio import ( "encoding/binary" "io" "google.golang.org/protobuf/proto" ) const uint32BinaryLen = 4 func NewUint32DelimitedWriter(w io.Writer, byteOrder binary.ByteOrder) WriteCloser { return &uint32Writer{w, byteOrder, nil, make([]byte, uint32BinaryLen)} } func NewSizeUint32DelimitedWriter(w io.Writer, byteOrder binary.ByteOrder, size int) WriteCloser { return &uint32Writer{w, byteOrder, make([]byte, size), make([]byte, uint32BinaryLen)} } type uint32Writer struct { w io.Writer byteOrder binary.ByteOrder buffer []byte lenBuf []byte } func (writer *uint32Writer) writeFallback(msg proto.Message) error { data, err := proto.Marshal(msg) if err != nil { return err } length := uint32(len(data)) writer.byteOrder.PutUint32(writer.lenBuf, length) if _, err = writer.w.Write(writer.lenBuf); err != nil { return err } _, err = writer.w.Write(data) return err } func (writer *uint32Writer) WriteMsg(msg proto.Message) error { m, ok := msg.(marshaler) if !ok { return writer.writeFallback(msg) } n, ok := getSize(m) if !ok { return writer.writeFallback(msg) } size := n + uint32BinaryLen if size > len(writer.buffer) { writer.buffer = make([]byte, size) } writer.byteOrder.PutUint32(writer.buffer, uint32(n)) if _, err := m.MarshalTo(writer.buffer[uint32BinaryLen:]); err != nil { return err } _, err := writer.w.Write(writer.buffer[:size]) return err } func (writer *uint32Writer) Close() error { if closer, ok := writer.w.(io.Closer); ok { return closer.Close() } return nil } type uint32Reader struct { r io.Reader byteOrder binary.ByteOrder lenBuf []byte buf []byte maxSize int } func NewUint32DelimitedReader(r io.Reader, byteOrder binary.ByteOrder, maxSize int) ReadCloser { return &uint32Reader{r, byteOrder, make([]byte, 4), nil, maxSize} } func (reader *uint32Reader) ReadMsg(msg proto.Message) error { if _, err := io.ReadFull(reader.r, reader.lenBuf); err != nil { return err } length32 := reader.byteOrder.Uint32(reader.lenBuf) length := int(length32) if length < 0 || length > reader.maxSize { return io.ErrShortBuffer } if length > len(reader.buf) { reader.buf = make([]byte, length) } _, err := io.ReadFull(reader.r, reader.buf[:length]) if err != nil { return err } return proto.Unmarshal(reader.buf[:length], msg) } func (reader *uint32Reader) Close() error { if closer, ok := reader.r.(io.Closer); ok { return closer.Close() } return nil } ================================================ FILE: pkg/protoio/varint.go ================================================ // Protocol Buffers for Go with Gadgets // // Copyright (c) 2013, The GoGo Authors. All rights reserved. // http://github.com/gogo/protobuf // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package protoio import ( "bufio" "encoding/binary" "io" "google.golang.org/protobuf/proto" ) func NewDelimitedWriter(w io.Writer) WriteCloser { return &varintWriter{w, make([]byte, binary.MaxVarintLen64), nil} } type varintWriter struct { w io.Writer lenBuf []byte buffer []byte } func (writer *varintWriter) WriteMsg(msg proto.Message) (err error) { var data []byte if m, ok := msg.(marshaler); ok { n, ok := getSize(m) if ok { if n+binary.MaxVarintLen64 >= len(writer.buffer) { writer.buffer = make([]byte, n+binary.MaxVarintLen64) } lenOff := binary.PutUvarint(writer.buffer, uint64(n)) _, err = m.MarshalTo(writer.buffer[lenOff:]) if err != nil { return err } _, err = writer.w.Write(writer.buffer[:lenOff+n]) return err } } // fallback data, err = proto.Marshal(msg) if err != nil { return err } length := uint64(len(data)) n := binary.PutUvarint(writer.lenBuf, length) _, err = writer.w.Write(writer.lenBuf[:n]) if err != nil { return err } _, err = writer.w.Write(data) return err } func (writer *varintWriter) Close() error { if closer, ok := writer.w.(io.Closer); ok { return closer.Close() } return nil } func NewDelimitedReader(r io.Reader, maxSize int) ReadCloser { var closer io.Closer if c, ok := r.(io.Closer); ok { closer = c } return &varintReader{bufio.NewReader(r), nil, maxSize, closer} } type varintReader struct { r *bufio.Reader buf []byte maxSize int closer io.Closer } func (reader *varintReader) ReadMsg(msg proto.Message) error { length64, err := binary.ReadUvarint(reader.r) if err != nil { return err } length := int(length64) if length < 0 || length > reader.maxSize { return io.ErrShortBuffer } if len(reader.buf) < length { reader.buf = make([]byte, length) } buf := reader.buf[:length] if _, err := io.ReadFull(reader.r, buf); err != nil { return err } return proto.Unmarshal(buf, msg) } func (reader *varintReader) Close() error { if reader.closer != nil { return reader.closer.Close() } return nil } ================================================ FILE: pkg/proximitytransport/addr.go ================================================ package proximitytransport import "net" // Addr is a net.Addr. var _ net.Addr = &Addr{} // Addr represents a network end point address. type Addr struct { Address string transport *proximityTransport } // Network returns the address's network name. func (b *Addr) Network() string { return b.transport.driver.ProtocolName() } // String return's the string form of the address. func (b *Addr) String() string { return b.Address } ================================================ FILE: pkg/proximitytransport/conn.go ================================================ package proximitytransport import ( "context" "fmt" "io" "net" "sync" "time" "github.com/libp2p/go-libp2p/core/network" peer "github.com/libp2p/go-libp2p/core/peer" tpt "github.com/libp2p/go-libp2p/core/transport" ma "github.com/multiformats/go-multiaddr" manet "github.com/multiformats/go-multiaddr/net" "github.com/pkg/errors" "go.uber.org/zap" "berty.tech/weshnet/v2/pkg/logutil" ) // Conn is a manet.Conn. var _ manet.Conn = &Conn{} // Conn is the equivalent of a net.Conn object. It is the // result of calling the Dial or Listen functions in this // package, with associated local and remote Multiaddrs. type Conn struct { readIn *io.PipeWriter readOut *io.PipeReader localMa ma.Multiaddr remoteMa ma.Multiaddr ready bool sync.Mutex cache *RingBufferMap mp *mplex ctx context.Context cancel func() transport *proximityTransport } // newConn returns an inbound or outbound tpt.CapableConn upgraded from a Conn. func newConn(ctx context.Context, t *proximityTransport, remoteMa ma.Multiaddr, remotePID peer.ID, netdir network.Direction, ) (tpt.CapableConn, error) { t.logger.Debug("newConn()", logutil.PrivateString("remoteMa", remoteMa.String()), zap.Bool("inbound", netdir == network.DirInbound)) if netdir == network.DirUnknown { return nil, fmt.Errorf("invalid network direction") } connScope, err := t.swarm.ResourceManager().OpenConnection(netdir, false, remoteMa) if err != nil { return nil, fmt.Errorf("resource manager blocked connection : %w", err) } // Creates a manet.Conn pr, pw := io.Pipe() connCtx, cancel := context.WithCancel(t.listener.ctx) maconn := &Conn{ readIn: pw, readOut: pr, localMa: t.listener.localMa, remoteMa: remoteMa, ready: false, cache: NewRingBufferMap(t.logger, 128), mp: newMplex(connCtx, t.logger), ctx: connCtx, cancel: cancel, transport: t, } // Stores the conn in connMap, will be deleted during conn.Close() t.connMapMutex.Lock() t.connMap[maconn.RemoteAddr().String()] = maconn t.connMapMutex.Unlock() // Configure mplex and run it maconn.mp.addInputCache(t.cache) maconn.mp.addInputCache(maconn.cache) maconn.mp.setOutput(pw) // Returns an upgraded CapableConn (muxed, addr filtered, secured, etc...) return t.upgrader.Upgrade(ctx, t, maconn, netdir, remotePID, connScope) } // Read reads data from the connection. // Timeout handled by the native driver. func (c *Conn) Read(payload []byte) (n int, err error) { c.transport.logger.Debug("Conn.Read", logutil.PrivateString("remoteAddr", c.RemoteAddr().String())) if c.ctx.Err() != nil { c.transport.logger.Error("Conn.Read failed: conn already closed") return 0, fmt.Errorf("error: Conn.Read failed: conn already closed") } n, err = c.readOut.Read(payload) if err != nil { err = errors.Wrap(err, "error: Conn.Read failed: native read failed") c.transport.logger.Error("Conn.Read", zap.Error(err)) } else { c.transport.logger.Debug("Conn.Read successful") } return n, err } // Write writes data to the connection. // Timeout handled by the native driver. func (c *Conn) Write(payload []byte) (n int, err error) { c.transport.logger.Debug("Conn.Write", logutil.PrivateString("remoteAddr", c.RemoteAddr().String()), logutil.PrivateBinary("payload", payload)) if c.ctx.Err() != nil { return 0, fmt.Errorf("error: Conn.Write failed: conn already closed") } // Set connection as ready and flush cached payloads if !c.isReady() { c.Lock() if !c.ready { c.ready = true } c.Unlock() go c.mp.run(c.RemoteAddr().String()) } // Write to the peer's device using native driver. if !c.transport.driver.SendToPeer(c.RemoteAddr().String(), payload) { c.transport.logger.Error("Conn.Write failed") return 0, fmt.Errorf("error: Conn.Write failed: native write failed") } c.transport.logger.Debug("Conn.Write successful") return len(payload), nil } // Close closes the connection. // Any blocked Read or Write operations will be unblocked and return errors. func (c *Conn) Close() error { c.transport.logger.Debug("Conn.Close()") c.cancel() // Closes read pipe c.readIn.Close() c.readOut.Close() // Removes conn from connmgr's connMap c.transport.connMapMutex.Lock() delete(c.transport.connMap, c.RemoteAddr().String()) c.transport.connMapMutex.Unlock() // Disconnect the driver c.transport.driver.CloseConnWithPeer(c.RemoteAddr().String()) return nil } // isReady tells if libp2p is ready to accept input connections func (c *Conn) isReady() bool { c.Lock() defer c.Unlock() return c.ready } // LocalAddr returns the local network address. func (c *Conn) LocalAddr() net.Addr { lAddr, _ := c.LocalMultiaddr().ValueForProtocol(c.transport.driver.ProtocolCode()) return &Addr{ Address: lAddr, transport: c.transport, } } // RemoteAddr returns the remote network address. func (c *Conn) RemoteAddr() net.Addr { rAddr, _ := c.RemoteMultiaddr().ValueForProtocol(c.transport.driver.ProtocolCode()) return &Addr{ Address: rAddr, transport: c.transport, } } // LocalMultiaddr returns the local Multiaddr associated // with this connection. func (c *Conn) LocalMultiaddr() ma.Multiaddr { return c.localMa } // RemoteMultiaddr returns the remote Multiaddr associated // with this connection. func (c *Conn) RemoteMultiaddr() ma.Multiaddr { return c.remoteMa } // Noop deadline methods, handled by the native driver. // SetDeadline does nothing func (c *Conn) SetDeadline(time.Time) error { return nil } // SetReadDeadline does nothing func (c *Conn) SetReadDeadline(time.Time) error { return nil } // SetWriteDeadline does nothing func (c *Conn) SetWriteDeadline(time.Time) error { return nil } ================================================ FILE: pkg/proximitytransport/example_test.go ================================================ package proximitytransport_test ================================================ FILE: pkg/proximitytransport/listener.go ================================================ package proximitytransport import ( "context" "errors" "net" network "github.com/libp2p/go-libp2p/core/network" peer "github.com/libp2p/go-libp2p/core/peer" tpt "github.com/libp2p/go-libp2p/core/transport" ma "github.com/multiformats/go-multiaddr" ) // Listener is a tpt.Listener. var _ tpt.Listener = &Listener{} // Listener is an interface closely resembling the net.Listener interface. The // only real difference is that Accept() returns Conn's of the type in this // package, and also exposes a Multiaddr method as opposed to a regular Addr // method. type Listener struct { transport *proximityTransport localMa ma.Multiaddr inboundConnReq chan connReq // Chan used to accept inbound conn. ctx context.Context cancel func() } // connReq holds data necessary for inbound conn creation. type connReq struct { remoteMa ma.Multiaddr remotePID peer.ID } // newListener starts the native driver then returns a new Listener. func newListener(ctx context.Context, localMa ma.Multiaddr, t *proximityTransport) *Listener { t.logger.Debug("newListener()") ctx, cancel := context.WithCancel(ctx) listener := &Listener{ transport: t, localMa: localMa, inboundConnReq: make(chan connReq), ctx: ctx, cancel: cancel, } // Starts the native driver. // If it failed, don't return a error because no other transport // on the libp2p node will be created. t.driver.Start(t.swarm.LocalPeer().String()) return listener } // Accept waits for and returns the next connection to the listener. // Returns a Multiaddr friendly Conn. func (l *Listener) Accept() (tpt.CapableConn, error) { for { select { case req := <-l.inboundConnReq: l.transport.logger.Debug("Listener.Accept(): incoming connection") conn, err := newConn(l.ctx, l.transport, req.remoteMa, req.remotePID, network.DirInbound) // If the newConn failed for some reason, Accept won't return an error // because otherwise it will close the listener if err == nil { return conn, nil } case <-l.ctx.Done(): return nil, errors.New("error: Listener.Accept failed: listener already closed") } } } // Close closes the listener. // Any blocked Accept operations will be unblocked and return errors. func (l *Listener) Close() error { l.transport.logger.Debug("Listener.Close()") l.cancel() // Stops the native driver. l.transport.driver.Stop() // Removes listener so transport can instantiate a new one later. l.transport.lock.Lock() l.transport.listener = nil l.transport.lock.Unlock() // Unregister this transport TransportMapMutex.Lock() delete(TransportMap, l.transport.driver.ProtocolName()) TransportMapMutex.Unlock() return nil } // Multiaddr returns the listener's (local) Multiaddr. func (l *Listener) Multiaddr() ma.Multiaddr { return l.localMa } // Addr returns the net.Listener's network address. func (l *Listener) Addr() net.Addr { lAddr, _ := l.localMa.ValueForProtocol(l.transport.driver.ProtocolCode()) return &Addr{ Address: lAddr, } } ================================================ FILE: pkg/proximitytransport/mplex.go ================================================ package proximitytransport /* The mplex struct as NOT relationship with libp2p mplex package!!! This mplex struct is a multiplexer taken multiple inputs for one output. You must initialize mplex by setting input and output before running it. There are two types of input: 1) RingBufferMap 2) builtin chan []byte There is only one type of output: *io.PipeWriter When you start mplex, its flushed buffers first in the order you set them, and read on its chan []byte. */ import ( "context" "io" "sync" "go.uber.org/zap" "berty.tech/weshnet/v2/pkg/logutil" ) type mplex struct { inputCaches []*RingBufferMap inputLock sync.Mutex input chan []byte output *io.PipeWriter ctx context.Context logger *zap.Logger } func newMplex(ctx context.Context, logger *zap.Logger) *mplex { logger = logger.Named("mplex") return &mplex{ input: make(chan []byte), ctx: ctx, logger: logger, } } func (m *mplex) setOutput(o *io.PipeWriter) { m.output = o } func (m *mplex) addInputCache(c *RingBufferMap) { m.inputLock.Lock() m.inputCaches = append(m.inputCaches, c) m.inputLock.Unlock() } func (m *mplex) write(s []byte) { m.logger.Debug("write", logutil.PrivateBinary("payload", s)) _, err := m.output.Write(s) if err != nil { m.logger.Error("write: write pipe error", zap.Error(err)) } else { m.logger.Debug("write: successful write pipe") } } // run flushes caches and read input channel func (m *mplex) run(peerID string) { m.logger.Debug("run: started") // flush caches m.inputLock.Lock() for _, cache := range m.inputCaches { m.logger.Debug("run: flushing one cache") payloads := cache.Flush(peerID) for payload := range payloads { m.write(payload) } } m.inputLock.Unlock() // read input m.logger.Debug("run: reading input channel") for { select { case payload := <-m.input: m.write(payload) case <-m.ctx.Done(): return } } } ================================================ FILE: pkg/proximitytransport/proximitydriver.go ================================================ package proximitytransport type ProximityDriver interface { // Start the native driver Start(localPID string) // Stop the native driver Stop() // Check if the native driver is connected to the remote peer DialPeer(remotePID string) bool // Send data to the remote peer SendToPeer(remotePID string, payload []byte) bool // Close the connection with the remote peer CloseConnWithPeer(remotePID string) // Return the multiaddress protocol code ProtocolCode() int // Return the multiaddress protocol name ProtocolName() string // Return the default multiaddress DefaultAddr() string } type NoopProximityDriver struct { protocolCode int protocolName string defaultAddr string } func NewNoopProximityDriver(protocolCode int, protocolName, defaultAddr string) *NoopProximityDriver { return &NoopProximityDriver{ protocolCode: protocolCode, protocolName: protocolName, defaultAddr: defaultAddr, } } func (d *NoopProximityDriver) Start(_ string) {} func (d *NoopProximityDriver) Stop() {} func (d *NoopProximityDriver) DialPeer(_ string) bool { return false } func (d *NoopProximityDriver) SendToPeer(_ string, _ []byte) bool { return false } func (d *NoopProximityDriver) CloseConnWithPeer(_ string) {} func (d *NoopProximityDriver) ProtocolCode() int { return d.protocolCode } func (d *NoopProximityDriver) ProtocolName() string { return d.protocolName } func (d *NoopProximityDriver) DefaultAddr() string { return d.defaultAddr } ================================================ FILE: pkg/proximitytransport/ringBuffer_map.go ================================================ package proximitytransport import ( "container/ring" "sync" "go.uber.org/zap" "berty.tech/weshnet/v2/pkg/logutil" ) // RingBufferMap is a map of string:ringBuffer(aka circular buffer) // The key is a peerID. type RingBufferMap struct { sync.Mutex cache map[string]*ringBuffer bufferSize int logger *zap.Logger } type ringBuffer struct { sync.Mutex buffer *ring.Ring } // NewRingBufferMap returns a new connMgr struct // The size argument is the number of packets to save in cache. func NewRingBufferMap(logger *zap.Logger, size int) *RingBufferMap { logger = logger.Named("RingBuffer") return &RingBufferMap{ cache: make(map[string]*ringBuffer), bufferSize: size, logger: logger, } } // Add adds the payload into a circular cache func (rbm *RingBufferMap) Add(peerID string, payload []byte) { rbm.logger.Debug("Add", logutil.PrivateString("peerID", peerID), logutil.PrivateBinary("payload", payload)) var rBuffer *ringBuffer rbm.Lock() rBuffer, ok := rbm.cache[peerID] rbm.Unlock() if !ok { rBuffer = &ringBuffer{ buffer: ring.New(rbm.bufferSize), } } rBuffer.Lock() rBuffer.buffer.Value = payload rBuffer.buffer = rBuffer.buffer.Next() rBuffer.Unlock() rbm.Lock() rbm.cache[peerID] = rBuffer rbm.Unlock() } // Flush puts the cache contents into a chan and clears it func (rbm *RingBufferMap) Flush(peerID string) <-chan []byte { rbm.logger.Debug("flushCache", logutil.PrivateString("peerID", peerID)) c := make(chan []byte) go func() { rbm.Lock() rBuffer, ok := rbm.cache[peerID] rbm.Unlock() if ok { rBuffer.Lock() for i := 0; i < rbm.bufferSize; i++ { payload, ok := rBuffer.buffer.Value.([]byte) if !ok { rBuffer.buffer = rBuffer.buffer.Next() continue } rbm.logger.Debug("flushCache", logutil.PrivateBinary("payload", payload)) c <- payload rBuffer.buffer.Value = nil rBuffer.buffer = rBuffer.buffer.Next() } rBuffer.Unlock() rbm.Lock() delete(rbm.cache, peerID) rbm.Unlock() } close(c) }() return c } // Delete cache entry func (rbm *RingBufferMap) Delete(peerID string) { rbm.logger.Debug("RingBufferMap: Delete called", logutil.PrivateString("peerID", peerID)) rbm.Lock() _, ok := rbm.cache[peerID] if ok { rbm.logger.Debug("RingBufferMap: Delete: cache found", logutil.PrivateString("peerID", peerID)) delete(rbm.cache, peerID) } rbm.Unlock() } ================================================ FILE: pkg/proximitytransport/transport.go ================================================ package proximitytransport import ( "context" "fmt" "sync" network "github.com/libp2p/go-libp2p/core/network" peer "github.com/libp2p/go-libp2p/core/peer" pstore "github.com/libp2p/go-libp2p/core/peerstore" tpt "github.com/libp2p/go-libp2p/core/transport" "github.com/libp2p/go-libp2p/p2p/net/swarm" ma "github.com/multiformats/go-multiaddr" mafmt "github.com/multiformats/go-multiaddr-fmt" "github.com/pkg/errors" "go.uber.org/zap" "berty.tech/weshnet/v2/pkg/logutil" ) // The ProximityTransport is a libp2p transport that initializes NativeDriver. // It allows connecting to nearby peers // proximityTransport is a tpt.transport. var _ tpt.Transport = &proximityTransport{} // proximityTransport is a ProximityTransport. var _ ProximityTransport = &proximityTransport{} // TransportMap prevents instantiating multiple Transport var TransportMap = make(map[string]*proximityTransport) // TransportMapMutex is the mutex for the TransportMap var var TransportMapMutex sync.RWMutex // Define log level for driver loggers const ( Verbose = iota Debug Info Warn Error ) type ProximityTransport interface { HandleFoundPeer(remotePID string) bool HandleLostPeer(remotePID string) ReceiveFromPeer(remotePID string, payload []byte) Log(level int, message string) } type proximityTransport struct { swarm *swarm.Swarm upgrader tpt.Upgrader connMap map[string]*Conn connMapMutex sync.RWMutex cache *RingBufferMap lock sync.RWMutex listener *Listener driver ProximityDriver logger *zap.Logger ctx context.Context } func NewTransport(ctx context.Context, l *zap.Logger, driver ProximityDriver) func(swarm *swarm.Swarm, u tpt.Upgrader) (*proximityTransport, error) { if l == nil { l = zap.NewNop() } l = l.Named("ProximityTransport") if driver == nil { l.Error("error: NewTransport: driver is nil") driver = &NoopProximityDriver{} } l.Debug("remi: transport.go: new Transport") return func(swarm *swarm.Swarm, u tpt.Upgrader) (*proximityTransport, error) { l.Debug("NewTransport called", zap.String("driver", driver.ProtocolName())) transport := &proximityTransport{ swarm: swarm, upgrader: u, connMap: make(map[string]*Conn), cache: NewRingBufferMap(l, 128), driver: driver, logger: l, ctx: ctx, } return transport, nil } } // Dial dials the peer at the remote address. // With proximity connections (e.g. MC, BLE, Nearby) you can only dial a device that is already connected with the native driver. func (t *proximityTransport) Dial(ctx context.Context, remoteMa ma.Multiaddr, remotePID peer.ID) (tpt.CapableConn, error) { // proximityTransport needs to have a running listener in order to dial other peer // because native driver is initialized during listener creation. t.lock.RLock() defer t.lock.RUnlock() if t.listener == nil { return nil, errors.New("error: proximityTransport.Dial: no active listener") } // remoteAddr is supposed to be equal to remotePID since with proximity transports: // multiaddr = // remoteAddr, err := remoteMa.ValueForProtocol(t.driver.ProtocolCode()) if err != nil || remoteAddr != remotePID.String() { return nil, errors.Wrap(err, "error: proximityTransport.Dial: wrong multiaddr") } // Check if native driver is already connected to peer's device. // With proximity connections you can't really dial, only auto-connect with peer nearby. if !t.driver.DialPeer(remoteAddr) { return nil, errors.New("error: proximityTransport.Dial: peer not connected through the native driver") } // Can't have two connections on the same multiaddr t.connMapMutex.RLock() _, ok := t.connMap[remoteAddr] t.connMapMutex.RUnlock() if ok { return nil, errors.New("error: proximityTransport.Dial: already connected to this address") } // Returns an outbound conn. return newConn(ctx, t, remoteMa, remotePID, network.DirOutbound) } // CanDial returns true if this transport believes it can dial the given // multiaddr. func (t *proximityTransport) CanDial(remoteMa ma.Multiaddr) bool { // multiaddr validation checker return mafmt.Base(t.driver.ProtocolCode()).Matches(remoteMa) } // Listen listens on the given multiaddr. // Proximity connections can't listen on more than one listener. func (t *proximityTransport) Listen(localMa ma.Multiaddr) (tpt.Listener, error) { // localAddr is supposed to be equal to the localPID // or to DefaultAddr since multiaddr == // localPID := t.swarm.LocalPeer().String() localAddr, err := localMa.ValueForProtocol(t.driver.ProtocolCode()) if err != nil || (localMa.String() != t.driver.DefaultAddr() && localAddr != localPID) { return nil, errors.Wrap(err, "error: proximityTransport.Listen: wrong multiaddr") } // Replaces default bind by local host peerID if localMa.String() == t.driver.DefaultAddr() { localMa, err = ma.NewMultiaddr(fmt.Sprintf("/%s/%s", t.driver.ProtocolName(), localPID)) if err != nil { // Should never append. panic(err) } } // If the a listener already exists for this driver, returns an error. TransportMapMutex.RLock() _, ok := TransportMap[t.driver.ProtocolName()] TransportMapMutex.RUnlock() t.lock.RLock() if ok || t.listener != nil { t.lock.RUnlock() return nil, errors.New("error: proximityTransport.Listen: one listener maximum") } t.lock.RUnlock() // Register this transport TransportMapMutex.Lock() TransportMap[t.driver.ProtocolName()] = t TransportMapMutex.Unlock() t.lock.Lock() defer t.lock.Unlock() t.listener = newListener(t.ctx, localMa, t) return t.listener, err } // ReceiveFromPeer is called by native driver when peer's device sent data. // If the connection is not found, data is added in the transport cache level. // If the connection is not activated yet, data is added in the connection cache level. // Cache are circular buffer, avoiding RAM memory attack. func (t *proximityTransport) ReceiveFromPeer(remotePID string, payload []byte) { t.logger.Debug("ReceiveFromPeer()", zap.String("remotePID", remotePID), logutil.PrivateBinary("payload", payload)) // copy value from driver data := make([]byte, len(payload)) copy(data, payload) t.connMapMutex.RLock() c, ok := t.connMap[remotePID] t.connMapMutex.RUnlock() if ok { // Put payload in the Conn cache if libp2p connection is not ready if !c.isReady() { c.Lock() if !c.ready { t.logger.Info("ReceiveFromPeer: connection is not ready to accept incoming packets, add it to cache") c.cache.Add(remotePID, data) c.Unlock() return } c.Unlock() } // Write the payload into pipe c.mp.input <- data } else { t.logger.Info("ReceiveFromPeer: no Conn found, put payload in cache") t.cache.Add(remotePID, data) } } // HandleFoundPeer is called by the native driver when a new peer is found. // Adds the peer in the PeerStore and initiates a connection with it func (t *proximityTransport) HandleFoundPeer(sRemotePID string) bool { t.logger.Debug("HandleFoundPeer", zap.String("remotePID", sRemotePID)) remotePID, err := peer.Decode(sRemotePID) if err != nil { t.logger.Error("HandleFoundPeer: wrong remote peerID") return false } remoteMa, err := ma.NewMultiaddr(fmt.Sprintf("/%s/%s", t.driver.ProtocolName(), sRemotePID)) if err != nil { // Should never occur panic(err) } // Checks if a listener is currently running. t.lock.RLock() if t.listener == nil || t.listener.ctx.Err() != nil { t.lock.RUnlock() t.logger.Error("HandleFoundPeer: listener not running") return false } // Get snapshot of listener listener := t.listener // unblock here to prevent blocking other APIs of Listener or Transport t.lock.RUnlock() // Adds peer to peerstore. t.swarm.Peerstore().AddAddr(remotePID, remoteMa, pstore.TempAddrTTL) // Delete previous cache if it exists t.cache.Delete(sRemotePID) // Peer with lexicographical smallest peerID inits libp2p connection. if listener.Addr().String() < sRemotePID { t.logger.Debug("HandleFoundPeer: outgoing libp2p connection") // Async connect so HandleFoundPeer can return and unlock the native driver. // Needed to read and write during the connect handshake. go func() { // Need to use listener than t.listener here to not have to check valid value of t.listener err := t.connect(listener.ctx, peer.AddrInfo{ ID: remotePID, Addrs: []ma.Multiaddr{remoteMa}, }) if err != nil { t.logger.Error("HandleFoundPeer: async connect error", zap.Error(err)) t.swarm.Peerstore().SetAddr(remotePID, remoteMa, -1) t.driver.CloseConnWithPeer(sRemotePID) } }() return true } t.logger.Debug("HandleFoundPeer: incoming libp2p connection") // Peer with lexicographical biggest peerID accepts incoming connection. // FIXME : consider to push this code in go routine to prevent blocking native driver select { case listener.inboundConnReq <- connReq{ remoteMa: remoteMa, remotePID: remotePID, }: return true case <-listener.ctx.Done(): return false } } // Adapted from https://github.com/libp2p/go-libp2p/blob/v0.38.1/p2p/host/basic/basic_host.go#L795 func (t *proximityTransport) connect(ctx context.Context, pi peer.AddrInfo) error { // absorb addresses into peerstore t.swarm.Peerstore().AddAddrs(pi.ID, pi.Addrs, pstore.TempAddrTTL) forceDirect, _ := network.GetForceDirectDial(ctx) canUseLimitedConn, _ := network.GetAllowLimitedConn(ctx) if !forceDirect { connectedness := t.swarm.Connectedness(pi.ID) if connectedness == network.Connected || (canUseLimitedConn && connectedness == network.Limited) { return nil } } _, err := t.swarm.DialPeer(ctx, pi.ID) return err } // HandleLostPeer is called by the native driver when the connection with the peer is lost. // Closes connections with the peer. func (t *proximityTransport) HandleLostPeer(sRemotePID string) { t.logger.Debug("HandleLostPeer", logutil.PrivateString("remotePID", sRemotePID)) remotePID, err := peer.Decode(sRemotePID) if err != nil { t.logger.Error("HandleLostPeer: wrong remote peerID") return } remoteMa, err := ma.NewMultiaddr(fmt.Sprintf("/%s/%s", t.driver.ProtocolName(), sRemotePID)) if err != nil { // Should never occur panic(err) } // Remove peer's address to peerstore. t.swarm.Peerstore().SetAddr(remotePID, remoteMa, -1) // Close the peer connection conns := t.swarm.ConnsToPeer(remotePID) for _, conn := range conns { if conn.RemoteMultiaddr().Equal(remoteMa) { conn.Close() } } } func (t *proximityTransport) Log(level int, message string) { switch level { case Verbose, Debug: t.logger.Debug(message) case Info: t.logger.Info(message) case Warn: t.logger.Warn(message) case Error: t.logger.Error(message) } } // Proxy returns true if this transport proxies. func (t *proximityTransport) Proxy() bool { return false } // Protocols returns the set of protocols handled by this transport. func (t *proximityTransport) Protocols() []int { return []int{t.driver.ProtocolCode()} } func (t *proximityTransport) String() string { return t.driver.ProtocolName() } ================================================ FILE: pkg/rendezvous/emitterio_sync_client.go ================================================ package rendezvous import ( "context" "encoding/json" "fmt" "io" "sync" "time" emitter "github.com/berty/emitter-go/v2" rendezvous "github.com/berty/go-libp2p-rendezvous" pb "github.com/berty/go-libp2p-rendezvous/pb" "github.com/libp2p/go-libp2p/core/peer" "github.com/multiformats/go-multiaddr" "go.uber.org/zap" "google.golang.org/protobuf/proto" ) type SyncClient interface { rendezvous.RendezvousSyncClient io.Closer } type registrationMessage struct { registration *rendezvous.Registration psDetails *EmitterPubSubSubscriptionDetails } type emitterClient struct { logger *zap.Logger client *emitter.Client ctx context.Context cancel context.CancelFunc usage int } type emitterClientManager struct { ctx context.Context cancel context.CancelFunc clients map[string]*emitterClient outChans map[string][]chan *rendezvous.Registration inChan chan *registrationMessage mu sync.Mutex logger *zap.Logger } func (e *emitterClientManager) Close() (err error) { e.mu.Lock() for _, client := range e.clients { client.Close() } e.cancel() e.mu.Unlock() return nil } func (e *emitterClient) Close() (err error) { e.cancel() e.client.Disconnect(time.Millisecond * 100) return nil } func (e *emitterClient) subscribeToServerUpdates(inChan chan *registrationMessage, psDetails *EmitterPubSubSubscriptionDetails) (err error) { e.logger.Debug("subscribing", zap.String("chan", psDetails.ChannelName)) return e.client.Subscribe(psDetails.ReadKey, psDetails.ChannelName, func(_ *emitter.Client, message emitter.Message) { reg := &pb.RegistrationRecord{} e.logger.Debug("receiving a message", zap.Any("topic", message.Topic())) err := proto.Unmarshal(message.Payload(), reg) if err != nil { e.logger.Error("unable to unmarshall ", zap.Error(err)) return } pid, err := peer.Decode(reg.Id) if err != nil { e.logger.Error("unable to decode ", zap.Error(err)) return } maddrs := make([]multiaddr.Multiaddr, len(reg.Addrs)) for i, addrBytes := range reg.Addrs { maddrs[i], err = multiaddr.NewMultiaddrBytes(addrBytes) if err != nil { e.logger.Error("unable to decode multiaddr bytes", zap.Error(err)) return } } var fields []string for _, maddr := range maddrs { fields = append(fields, maddr.String()) } e.logger.Debug("receiving a peer", zap.String("topic", reg.Ns), zap.String("peer", pid.String()), zap.Strings("maddrs", fields)) inChan <- ®istrationMessage{ registration: &rendezvous.Registration{ Peer: peer.AddrInfo{ ID: pid, Addrs: maddrs, }, Ns: reg.Ns, Ttl: int(reg.Ttl), }, psDetails: psDetails, } }, emitter.WithLast(1)) } func (e *emitterClientManager) getClient(psDetails *EmitterPubSubSubscriptionDetails) (client *emitterClient, err error) { var ok bool if client, ok = e.clients[psDetails.ServerAddr]; ok { return } noophandler := func(*emitter.Client, emitter.Message) {} cl, err := emitter.Connect(psDetails.ServerAddr, noophandler, emitter.WithLogger(e.logger.Named("cl"))) if err != nil { return } wrapCtx, cancel := context.WithCancel(context.Background()) client = &emitterClient{ logger: e.logger.Named("cl"), ctx: wrapCtx, cancel: cancel, client: cl, } e.clients[psDetails.ServerAddr] = client return } func (e *emitterClientManager) Subscribe(ctx context.Context, psDetailsStr string) (<-chan *rendezvous.Registration, error) { psDetails := &EmitterPubSubSubscriptionDetails{} err := json.Unmarshal([]byte(psDetailsStr), psDetails) if err != nil { return nil, fmt.Errorf("unable to decode json: %w", err) } e.mu.Lock() defer e.mu.Unlock() client, err := e.getClient(psDetails) if err != nil { return nil, fmt.Errorf("unable to : %w", err) } sliceName := makeOutChanName(psDetails.ServerAddr, psDetails.ChannelName) if _, ok := e.outChans[sliceName]; !ok { e.logger.Debug("subscribing", zap.String("channel", psDetails.ChannelName), zap.String("target", psDetails.ServerAddr)) if err := client.subscribeToServerUpdates(e.inChan, psDetails); err != nil { return nil, fmt.Errorf("unable to subscribe to broker's channel: %w", err) } } ch := make(chan *rendezvous.Registration) e.outChans[sliceName] = append(e.outChans[sliceName], ch) client.usage++ go func() { select { case <-ctx.Done(): case <-client.ctx.Done(): } e.unsubscribe(psDetails, ch) close(ch) }() return ch, nil } type EmitterClientOptions struct { Logger *zap.Logger } func NewEmitterClient(opts *EmitterClientOptions) SyncClient { if opts == nil { opts = &EmitterClientOptions{} } if opts.Logger == nil { opts.Logger = zap.NewNop() } ctx, cancel := context.WithCancel(context.Background()) manager := &emitterClientManager{ ctx: ctx, cancel: cancel, clients: map[string]*emitterClient{}, logger: opts.Logger.Named("emitter"), outChans: map[string][]chan *rendezvous.Registration{}, inChan: make(chan *registrationMessage), } manager.logger.Debug("starting rdvp emitter client") go func() { for { select { case <-ctx.Done(): close(manager.inChan) return case registration := <-manager.inChan: outChanName := makeOutChanName(registration.psDetails.ServerAddr, registration.psDetails.ChannelName) manager.mu.Lock() channels, ok := manager.outChans[outChanName] manager.mu.Unlock() if !ok { manager.logger.Warn("received an event for an unknown channel", zap.String("channel", registration.psDetails.ChannelName)) continue } for _, ch := range channels { ch <- registration.registration } } } }() return manager } func (e *emitterClientManager) GetServiceType() string { return EmitterServiceType } func makeOutChanName(serverAddr, channelName string) string { return fmt.Sprintf("%s:%s", serverAddr, channelName) } func (e *emitterClientManager) unsubscribe(details *EmitterPubSubSubscriptionDetails, ch chan *rendezvous.Registration) { e.mu.Lock() defer e.mu.Unlock() sliceName := makeOutChanName(details.ServerAddr, details.ChannelName) for i, outChan := range e.outChans[sliceName] { if outChan == ch { e.outChans[sliceName] = append(e.outChans[sliceName][:i], e.outChans[sliceName][i+1:]...) break } } client, err := e.getClient(details) if err != nil { e.logger.Error("unable to find client", zap.Error(err)) return } if len(e.outChans[sliceName]) == 0 { if err := client.client.Unsubscribe(details.ReadKey, details.ChannelName); err != nil { e.logger.Error("unable to unsubscribe from client", zap.Error(err)) } } client.usage-- if client.usage <= 0 { e.clients[details.ServerAddr].cancel() delete(e.clients, details.ServerAddr) } } ================================================ FILE: pkg/rendezvous/emitterio_sync_provider.go ================================================ package rendezvous import ( "encoding/json" "fmt" "math/big" "time" emitter "github.com/berty/emitter-go/v2" rendezvous "github.com/berty/go-libp2p-rendezvous" pb "github.com/berty/go-libp2p-rendezvous/pb" "github.com/libp2p/go-libp2p/core/peer" "go.uber.org/zap" "google.golang.org/protobuf/proto" ) const EmitterServiceType = "emitter-io" type EmitterPubSub struct { client *emitter.Client adminKey string serverAddr string publicaddr string logger *zap.Logger } type EmitterPubSubSubscriptionDetails struct { ServerAddr string ReadKey string ChannelName string } type EmitterOptions struct { Logger *zap.Logger ServerPublicAddr string EmitterOptions []func(*emitter.Client) } func NewEmitterServer(serverAddr string, adminKey string, options *EmitterOptions) (*EmitterPubSub, error) { if options == nil { options = &EmitterOptions{} } if options.ServerPublicAddr == "" { options.ServerPublicAddr = serverAddr } if options.Logger == nil { options.Logger = zap.NewNop() } options.Logger = options.Logger.Named("emitter") c, err := emitter.Connect(serverAddr, func(_ *emitter.Client, msg emitter.Message) { options.Logger.Debug("emitter received msg", zap.String("topic", msg.Topic())) }, emitter.WithLogger(options.Logger.Named("cl"))) if err != nil { options.Logger.Error("error on `Connect`", zap.Error(err)) } options.Logger.Debug("connected to emitter", zap.String("target", serverAddr)) ps := &EmitterPubSub{ client: c, adminKey: adminKey, logger: options.Logger, publicaddr: options.ServerPublicAddr, serverAddr: serverAddr, } return ps, nil } // nolint:revive func (p *EmitterPubSub) Register(pid peer.ID, ns string, addrs [][]byte, ttlAsSeconds int, counter uint64) { p.logger.Debug("register", zap.String("pid", pid.String()), zap.String("ns", ns)) channel := channelForRendezvousPoint(ns) writeKey, err := p.writeKeyForChannel(channel) if err != nil { p.logger.Error("unable to create topic for NS", zap.Error(err)) return } dataToSend := &pb.RegistrationRecord{ Id: pid.String(), Addrs: addrs, Ns: ns, Ttl: time.Now().Add(time.Duration(ttlAsSeconds) * time.Second).UnixMilli(), } marshaled, err := proto.Marshal(dataToSend) if err != nil { p.logger.Error("unable to marshal proto", zap.Error(err)) return } if err := p.client.PublishWithTTL(writeKey, channel, marshaled, ttlAsSeconds); err != nil { p.logger.Error("unable to publish on channel", zap.Error(err)) return } p.logger.Debug("publishing done", zap.String("pid", pid.String()), zap.String("channel", channel)) } func (p *EmitterPubSub) Unregister(_ peer.ID, _ string) { p.logger.Debug("unsupported method unregister") } func (p *EmitterPubSub) Subscribe(ns string) (string, error) { channel := channelForRendezvousPoint(ns) readKey, err := p.readKeysForChannel(channel) if err != nil { return "", err } jsonData, err := json.Marshal(&EmitterPubSubSubscriptionDetails{ ServerAddr: p.publicaddr, ReadKey: readKey, ChannelName: channel, }) if err != nil { return "", err } return string(jsonData), nil } func (p *EmitterPubSub) GetServiceType() string { return EmitterServiceType } func (p *EmitterPubSub) Close() error { p.client.Disconnect(time.Second) return nil } func (p *EmitterPubSub) writeKeyForChannel(channelName string) (string, error) { return p.keyForChannel(channelName, "w") } func (p *EmitterPubSub) readKeysForChannel(channelName string) (string, error) { return p.keyForChannel(channelName, "r") } func (p *EmitterPubSub) keyForChannel(channelName string, permissions string) (string, error) { key, err := p.client.GenerateKey(p.adminKey, channelName, permissions, int(time.Hour.Seconds()*24)) if err != nil { return "", fmt.Errorf("unable to generate key: %w", err) } return key, nil } func channelForRendezvousPoint(topic string) string { // force base62 encoded topic (without '+' and '#' that are wildcard in the mqtt world) topic = toBase62(topic) return fmt.Sprintf("rdvp/%s/", topic) } func toBase62(topic string) string { var i big.Int i.SetBytes([]byte(topic)) return i.Text(62) } var ( _ rendezvous.RendezvousSync = (*EmitterPubSub)(nil) _ rendezvous.RendezvousSyncSubscribable = (*EmitterPubSub)(nil) ) ================================================ FILE: pkg/rendezvous/emitterio_sync_test.go ================================================ package rendezvous_test import ( "context" "os" "sync" "sync/atomic" "testing" "time" rendezvous "github.com/berty/go-libp2p-rendezvous" db "github.com/berty/go-libp2p-rendezvous/db/sqlcipher" "github.com/berty/go-libp2p-rendezvous/test_utils" "github.com/libp2p/go-libp2p/core/host" mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" "github.com/stretchr/testify/require" "go.uber.org/zap" berty_rendezvous "berty.tech/weshnet/v2/pkg/rendezvous" ) func MakeRendezvousServiceTest(ctx context.Context, host host.Host, path string, rzs ...rendezvous.RendezvousSync) (*rendezvous.RendezvousService, error) { dbi, err := db.OpenDB(ctx, path) if err != nil { return nil, err } return rendezvous.NewRendezvousService(host, dbi, rzs...), nil } func getEmitterRendezvousClients(ctx context.Context, t *testing.T, hosts []host.Host) []rendezvous.RendezvousClient { t.Helper() clients := make([]rendezvous.RendezvousClient, len(hosts)-1) for i, host := range hosts[1:] { syncClient := berty_rendezvous.NewEmitterClient(nil) t.Cleanup(func() { syncClient.Close() }) clients[i] = rendezvous.NewRendezvousClient(host, hosts[0].ID(), syncClient) } return clients } func TestEmitterIOFlow(t *testing.T) { // @NOTE(gfanton): see tools/emitter-server to test it // TEST_EMITTER_SERVER_ADDR= TEST_EMITTER_ADMINKEY= go test . const topic = "foo1" serverAddr := os.Getenv("TEST_EMITTER_SERVER_ADDR") adminKey := os.Getenv("TEST_EMITTER_ADMINKEY") if adminKey == "" || serverAddr == "" { t.Skip("cannot test emitter, no adminKey/serverAddr provided") } ctx, cancel := context.WithCancel(context.Background()) defer cancel() mn := mocknet.New() defer mn.Close() // Instantiate server and clients hosts := test_utils.GetRendezvousHosts(t, ctx, mn, 4) for _, h := range hosts { t.Cleanup(func() { _ = h.Close() }) } logger, err := zap.NewDevelopment() require.NoError(t, err) emitterPubSubSync, err := berty_rendezvous.NewEmitterServer(serverAddr, adminKey, &berty_rendezvous.EmitterOptions{ Logger: logger.Named("emitter"), }) require.NoError(t, err) defer emitterPubSubSync.Close() svc, err := MakeRendezvousServiceTest(ctx, hosts[0], ":memory:", emitterPubSubSync) require.NoError(t, err) defer svc.DB.Close() clients := getEmitterRendezvousClients(ctx, t, hosts) regFound := int64(0) wg := sync.WaitGroup{} const announcementCount = 5 for _, client := range clients[1:] { wg.Add(1) ctx, cancel := context.WithTimeout(ctx, time.Second*5) ch, err := client.DiscoverSubscribe(ctx, topic) require.NoError(t, err) go func() { regFoundForPeer := 0 defer cancel() defer wg.Done() for p := range ch { if test_utils.CheckPeerInfo(t, p, hosts[2], false) == true { regFoundForPeer++ atomic.AddInt64(®Found, 1) } if regFoundForPeer == announcementCount { go func() { // this allows more events to be received time.Sleep(time.Millisecond * 500) cancel() }() } } }() } for i := 0; i < announcementCount; i++ { _, err = clients[1].Register(ctx, topic, rendezvous.DefaultTTL) require.NoError(t, err) } wg.Wait() if regFound != int64(len(clients[1:]))*announcementCount { require.FailNowf(t, "number of records doesn't match", "expected %d records to be found got %d", int64(len(clients[1:])), regFound) } } ================================================ FILE: pkg/rendezvous/rendezvous.go ================================================ package rendezvous import ( "crypto/hmac" "crypto/sha256" "encoding/binary" "time" ) const DefaultRotationInterval = time.Hour * 24 func RoundTimePeriod(date time.Time, interval time.Duration) time.Time { if interval < 0 { interval = -interval } intervalSeconds := int64(interval.Seconds()) periodsElapsed := date.Unix() / intervalSeconds totalTime := periodsElapsed * intervalSeconds return time.Unix(totalTime, 0).In(date.Location()) } func NextTimePeriod(date time.Time, interval time.Duration) time.Time { if interval < 0 { interval = -interval } return RoundTimePeriod(date, interval).Add(interval) } func GenerateRendezvousPointForPeriod(topic, seed []byte, date time.Time) []byte { buf := make([]byte, 8) mac := hmac.New(sha256.New, append(topic, seed...)) binary.BigEndian.PutUint64(buf, uint64(date.Unix())) // hash.Hash.Write never returns an error, as documented at // https://pkg.go.dev/hash#Hash mac.Write(buf) //nolint:errcheck sum := mac.Sum(nil) return sum } ================================================ FILE: pkg/rendezvous/rendezvous_test.go ================================================ package rendezvous_test import ( "encoding/hex" "fmt" "testing" "time" "github.com/stretchr/testify/require" "berty.tech/weshnet/v2/pkg/rendezvous" ) func TestRoundTimePeriod_Next(t *testing.T) { cases := []struct { time time.Time period time.Duration out time.Time next time.Time }{ { time: time.Date(2020, 4, 10, 12, 0, 0, 0, time.UTC), period: time.Hour, out: time.Date(2020, 4, 10, 12, 0, 0, 0, time.UTC), next: time.Date(2020, 4, 10, 13, 0, 0, 0, time.UTC), }, { time: time.Date(2020, 4, 10, 12, 0, 0, 0, time.UTC), period: -time.Hour, out: time.Date(2020, 4, 10, 12, 0, 0, 0, time.UTC), next: time.Date(2020, 4, 10, 13, 0, 0, 0, time.UTC), }, { time: time.Date(2020, 4, 10, 12, 34, 56, 0, time.UTC), period: time.Hour, out: time.Date(2020, 4, 10, 12, 0, 0, 0, time.UTC), next: time.Date(2020, 4, 10, 13, 0, 0, 0, time.UTC), }, } for i, tc := range cases { t.Run(fmt.Sprintf("tc: %d", i), func(t *testing.T) { rounded := rendezvous.RoundTimePeriod(tc.time, tc.period) require.Equal(t, tc.out, rounded) next := rendezvous.NextTimePeriod(tc.time, tc.period) require.Equal(t, tc.next, next) }) } } func TestGenerateRendezvousPointForPeriod(t *testing.T) { baseTimeA := time.Date(2020, 4, 10, 12, 0, 0, 0, time.UTC) baseTimeB := time.Date(2020, 4, 10, 13, 0, 0, 0, time.UTC) cases := []struct { topic []byte seed []byte expected string // as hex string time time.Time }{ { topic: []byte("topicA"), seed: []byte("seedA"), expected: "8b7fdc831ca90f78995f32d8b9cf7bc8682a7fc250fe13a9b7c5c0851a3b8cbc", time: baseTimeA, }, { topic: []byte("topicA"), seed: []byte("seedA"), expected: "ec86e0cb471195733ebbeb04277aafcfc60a19c09195cf04e60b50857465c27f", time: baseTimeB, }, { topic: []byte("topicA"), seed: []byte("seedB"), expected: "f87f5dfc4e8a68be75d6008ab3aa0a4295b6049e9ec21f03d0c1410895171683", time: baseTimeA, }, { topic: []byte("topicB"), seed: []byte("seedA"), expected: "6350bd507198a9acb816dcadae8c5c6ff6c96ee6c9606c8ebe15c1f645ac4c4e", time: baseTimeA, }, } for i, tc := range cases { t.Run(fmt.Sprintf("tc: %d", i), func(t *testing.T) { point := rendezvous.GenerateRendezvousPointForPeriod(tc.topic, tc.seed, tc.time) require.Equal(t, tc.expected, hex.EncodeToString(point)) }) } } ================================================ FILE: pkg/rendezvous/rotation.go ================================================ package rendezvous import ( "encoding/base64" "fmt" "sync" "time" ) var ( RotationGracePeriod = time.Minute * 10 MinimumDelayRotation = time.Minute ) type RotationInterval struct { interval time.Duration cacheTopics map[string]*Point cacheRotations map[string]*Point muCache sync.RWMutex } func NewStaticRotationInterval() *RotationInterval { // from https://stackoverflow.com/a/32620397 maxTime := time.Unix(1<<63-62135596801, 999999999) return NewRotationInterval(time.Until(maxTime)) } func NewRotationInterval(interval time.Duration) *RotationInterval { return &RotationInterval{ interval: interval, cacheTopics: make(map[string]*Point), cacheRotations: make(map[string]*Point), } } func (r *RotationInterval) RegisterRotation(at time.Time, topic string, seed []byte) { point := r.NewRendezvousPointForPeriod(at, topic, seed) r.muCache.Lock() r.registerPoint(point) r.muCache.Unlock() } func (r *RotationInterval) RoundTimePeriod(at time.Time) time.Time { return RoundTimePeriod(at, r.interval) } func (r *RotationInterval) NextTimePeriod(at time.Time) time.Time { return NextTimePeriod(at, r.interval) } func (r *RotationInterval) PointForRawRotation(rotation []byte) (*Point, error) { return r.PointForRotation(base64.StdEncoding.EncodeToString(rotation)) } func (r *RotationInterval) PointForRotation(rotation string) (*Point, error) { r.muCache.Lock() defer r.muCache.Unlock() if point, ok := r.cacheRotations[rotation]; ok { if point.IsExpired() { point = r.rotate(point, DefaultRotationInterval) } return point, nil } return nil, fmt.Errorf("unable to get rendez vous, no matching rotation registered") } func (r *RotationInterval) PointForTopic(topic string) (*Point, error) { r.muCache.Lock() defer r.muCache.Unlock() if point, ok := r.cacheTopics[topic]; ok { if point.IsExpired() { point = r.rotate(point, DefaultRotationInterval) } return point, nil } return nil, fmt.Errorf("unable to get rendezvous point, no matching topic registered") } func (r *RotationInterval) NewRendezvousPointForPeriod(at time.Time, topic string, seed []byte) (point *Point) { at = r.RoundTimePeriod(at) rotation := GenerateRendezvousPointForPeriod([]byte(topic), seed, at) next := r.NextTimePeriod(at) return &Point{ rp: r, rotation: rotation, topic: topic, seed: seed, deadline: next, } } func (r *RotationInterval) registerPoint(point *Point) { keytopic, keyrotation := point.keys() r.cacheTopics[keytopic] = point r.cacheRotations[keyrotation] = point } func (r *RotationInterval) rotate(old *Point, graceperiod time.Duration) *Point { newPoint := old.NextPoint() // register new point r.registerPoint(newPoint) cleanuptime := max(time.Until(newPoint.Deadline().Add(graceperiod)), 0) // cleanup after the grace period time.AfterFunc(cleanuptime, func() { r.muCache.Lock() _, keyrotation := old.keys() delete(r.cacheRotations, keyrotation) r.muCache.Unlock() }) return newPoint } type Point struct { rp *RotationInterval topic string rotation []byte seed []byte deadline time.Time } func (p *Point) NextPoint() *Point { if p.IsExpired() { return p.rp.NewRendezvousPointForPeriod(time.Now(), p.topic, p.seed) } return p.rp.NewRendezvousPointForPeriod(p.deadline.Add(time.Second), p.topic, p.seed) } func (p *Point) Seed() []byte { return p.seed } func (p *Point) keys() (topic string, rotation string) { topic = p.Topic() rotation = p.RotationTopic() return } func (p *Point) Topic() string { return p.topic } func (p *Point) RawTopic() []byte { return []byte(p.topic) } func (p *Point) RotationTopic() string { return base64.StdEncoding.EncodeToString(p.rotation) } func (p *Point) RawRotationTopic() []byte { return p.rotation } func (p *Point) Deadline() time.Time { return p.deadline } func (p *Point) TTL() time.Duration { return time.Until(p.deadline) } func (p *Point) IsExpired() bool { return p.TTL() > 0 } ================================================ FILE: pkg/replicationtypes/bertyreplication.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.34.2 // protoc (unknown) // source: replicationtypes/bertyreplication.proto package replicationtypes import ( protocoltypes "berty.tech/weshnet/v2/pkg/protocoltypes" _ "github.com/srikrsna/protoc-gen-gotag/tagger" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type ReplicatedGroup struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields PublicKey string `protobuf:"bytes,1,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty" gorm:"primaryKey"` SignPub string `protobuf:"bytes,2,opt,name=sign_pub,json=signPub,proto3" json:"sign_pub,omitempty"` LinkKey string `protobuf:"bytes,3,opt,name=link_key,json=linkKey,proto3" json:"link_key,omitempty"` CreatedAt int64 `protobuf:"varint,100,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` UpdatedAt int64 `protobuf:"varint,101,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"` MetadataEntriesCount int64 `protobuf:"varint,102,opt,name=metadata_entries_count,json=metadataEntriesCount,proto3" json:"metadata_entries_count,omitempty"` MetadataLatestHead string `protobuf:"bytes,103,opt,name=metadata_latest_head,json=metadataLatestHead,proto3" json:"metadata_latest_head,omitempty"` MessageEntriesCount int64 `protobuf:"varint,104,opt,name=message_entries_count,json=messageEntriesCount,proto3" json:"message_entries_count,omitempty"` MessageLatestHead string `protobuf:"bytes,105,opt,name=message_latest_head,json=messageLatestHead,proto3" json:"message_latest_head,omitempty"` } func (x *ReplicatedGroup) Reset() { *x = ReplicatedGroup{} if protoimpl.UnsafeEnabled { mi := &file_replicationtypes_bertyreplication_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ReplicatedGroup) String() string { return protoimpl.X.MessageStringOf(x) } func (*ReplicatedGroup) ProtoMessage() {} func (x *ReplicatedGroup) ProtoReflect() protoreflect.Message { mi := &file_replicationtypes_bertyreplication_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ReplicatedGroup.ProtoReflect.Descriptor instead. func (*ReplicatedGroup) Descriptor() ([]byte, []int) { return file_replicationtypes_bertyreplication_proto_rawDescGZIP(), []int{0} } func (x *ReplicatedGroup) GetPublicKey() string { if x != nil { return x.PublicKey } return "" } func (x *ReplicatedGroup) GetSignPub() string { if x != nil { return x.SignPub } return "" } func (x *ReplicatedGroup) GetLinkKey() string { if x != nil { return x.LinkKey } return "" } func (x *ReplicatedGroup) GetCreatedAt() int64 { if x != nil { return x.CreatedAt } return 0 } func (x *ReplicatedGroup) GetUpdatedAt() int64 { if x != nil { return x.UpdatedAt } return 0 } func (x *ReplicatedGroup) GetMetadataEntriesCount() int64 { if x != nil { return x.MetadataEntriesCount } return 0 } func (x *ReplicatedGroup) GetMetadataLatestHead() string { if x != nil { return x.MetadataLatestHead } return "" } func (x *ReplicatedGroup) GetMessageEntriesCount() int64 { if x != nil { return x.MessageEntriesCount } return 0 } func (x *ReplicatedGroup) GetMessageLatestHead() string { if x != nil { return x.MessageLatestHead } return "" } type ReplicatedGroupToken struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields ReplicatedGroupPublicKey string `protobuf:"bytes,1,opt,name=replicated_group_public_key,json=replicatedGroupPublicKey,proto3" json:"replicated_group_public_key,omitempty" gorm:"index;primaryKey;autoIncrement:false"` ReplicatedGroup *ReplicatedGroup `protobuf:"bytes,2,opt,name=replicated_group,json=replicatedGroup,proto3" json:"replicated_group,omitempty"` TokenIssuer string `protobuf:"bytes,3,opt,name=token_issuer,json=tokenIssuer,proto3" json:"token_issuer,omitempty" gorm:"primaryKey;autoIncrement:false"` TokenId string `protobuf:"bytes,4,opt,name=token_id,json=tokenId,proto3" json:"token_id,omitempty" gorm:"primaryKey;autoIncrement:false"` CreatedAt int64 `protobuf:"varint,5,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` } func (x *ReplicatedGroupToken) Reset() { *x = ReplicatedGroupToken{} if protoimpl.UnsafeEnabled { mi := &file_replicationtypes_bertyreplication_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ReplicatedGroupToken) String() string { return protoimpl.X.MessageStringOf(x) } func (*ReplicatedGroupToken) ProtoMessage() {} func (x *ReplicatedGroupToken) ProtoReflect() protoreflect.Message { mi := &file_replicationtypes_bertyreplication_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ReplicatedGroupToken.ProtoReflect.Descriptor instead. func (*ReplicatedGroupToken) Descriptor() ([]byte, []int) { return file_replicationtypes_bertyreplication_proto_rawDescGZIP(), []int{1} } func (x *ReplicatedGroupToken) GetReplicatedGroupPublicKey() string { if x != nil { return x.ReplicatedGroupPublicKey } return "" } func (x *ReplicatedGroupToken) GetReplicatedGroup() *ReplicatedGroup { if x != nil { return x.ReplicatedGroup } return nil } func (x *ReplicatedGroupToken) GetTokenIssuer() string { if x != nil { return x.TokenIssuer } return "" } func (x *ReplicatedGroupToken) GetTokenId() string { if x != nil { return x.TokenId } return "" } func (x *ReplicatedGroupToken) GetCreatedAt() int64 { if x != nil { return x.CreatedAt } return 0 } type ReplicationServiceReplicateGroup struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *ReplicationServiceReplicateGroup) Reset() { *x = ReplicationServiceReplicateGroup{} if protoimpl.UnsafeEnabled { mi := &file_replicationtypes_bertyreplication_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ReplicationServiceReplicateGroup) String() string { return protoimpl.X.MessageStringOf(x) } func (*ReplicationServiceReplicateGroup) ProtoMessage() {} func (x *ReplicationServiceReplicateGroup) ProtoReflect() protoreflect.Message { mi := &file_replicationtypes_bertyreplication_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ReplicationServiceReplicateGroup.ProtoReflect.Descriptor instead. func (*ReplicationServiceReplicateGroup) Descriptor() ([]byte, []int) { return file_replicationtypes_bertyreplication_proto_rawDescGZIP(), []int{2} } type ReplicateGlobalStats struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *ReplicateGlobalStats) Reset() { *x = ReplicateGlobalStats{} if protoimpl.UnsafeEnabled { mi := &file_replicationtypes_bertyreplication_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ReplicateGlobalStats) String() string { return protoimpl.X.MessageStringOf(x) } func (*ReplicateGlobalStats) ProtoMessage() {} func (x *ReplicateGlobalStats) ProtoReflect() protoreflect.Message { mi := &file_replicationtypes_bertyreplication_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ReplicateGlobalStats.ProtoReflect.Descriptor instead. func (*ReplicateGlobalStats) Descriptor() ([]byte, []int) { return file_replicationtypes_bertyreplication_proto_rawDescGZIP(), []int{3} } type ReplicateGroupStats struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *ReplicateGroupStats) Reset() { *x = ReplicateGroupStats{} if protoimpl.UnsafeEnabled { mi := &file_replicationtypes_bertyreplication_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ReplicateGroupStats) String() string { return protoimpl.X.MessageStringOf(x) } func (*ReplicateGroupStats) ProtoMessage() {} func (x *ReplicateGroupStats) ProtoReflect() protoreflect.Message { mi := &file_replicationtypes_bertyreplication_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ReplicateGroupStats.ProtoReflect.Descriptor instead. func (*ReplicateGroupStats) Descriptor() ([]byte, []int) { return file_replicationtypes_bertyreplication_proto_rawDescGZIP(), []int{4} } type ReplicationServiceReplicateGroup_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Group *protocoltypes.Group `protobuf:"bytes,1,opt,name=group,proto3" json:"group,omitempty"` } func (x *ReplicationServiceReplicateGroup_Request) Reset() { *x = ReplicationServiceReplicateGroup_Request{} if protoimpl.UnsafeEnabled { mi := &file_replicationtypes_bertyreplication_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ReplicationServiceReplicateGroup_Request) String() string { return protoimpl.X.MessageStringOf(x) } func (*ReplicationServiceReplicateGroup_Request) ProtoMessage() {} func (x *ReplicationServiceReplicateGroup_Request) ProtoReflect() protoreflect.Message { mi := &file_replicationtypes_bertyreplication_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ReplicationServiceReplicateGroup_Request.ProtoReflect.Descriptor instead. func (*ReplicationServiceReplicateGroup_Request) Descriptor() ([]byte, []int) { return file_replicationtypes_bertyreplication_proto_rawDescGZIP(), []int{2, 0} } func (x *ReplicationServiceReplicateGroup_Request) GetGroup() *protocoltypes.Group { if x != nil { return x.Group } return nil } type ReplicationServiceReplicateGroup_Reply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Ok bool `protobuf:"varint,1,opt,name=ok,proto3" json:"ok,omitempty"` } func (x *ReplicationServiceReplicateGroup_Reply) Reset() { *x = ReplicationServiceReplicateGroup_Reply{} if protoimpl.UnsafeEnabled { mi := &file_replicationtypes_bertyreplication_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ReplicationServiceReplicateGroup_Reply) String() string { return protoimpl.X.MessageStringOf(x) } func (*ReplicationServiceReplicateGroup_Reply) ProtoMessage() {} func (x *ReplicationServiceReplicateGroup_Reply) ProtoReflect() protoreflect.Message { mi := &file_replicationtypes_bertyreplication_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ReplicationServiceReplicateGroup_Reply.ProtoReflect.Descriptor instead. func (*ReplicationServiceReplicateGroup_Reply) Descriptor() ([]byte, []int) { return file_replicationtypes_bertyreplication_proto_rawDescGZIP(), []int{2, 1} } func (x *ReplicationServiceReplicateGroup_Reply) GetOk() bool { if x != nil { return x.Ok } return false } type ReplicateGlobalStats_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *ReplicateGlobalStats_Request) Reset() { *x = ReplicateGlobalStats_Request{} if protoimpl.UnsafeEnabled { mi := &file_replicationtypes_bertyreplication_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ReplicateGlobalStats_Request) String() string { return protoimpl.X.MessageStringOf(x) } func (*ReplicateGlobalStats_Request) ProtoMessage() {} func (x *ReplicateGlobalStats_Request) ProtoReflect() protoreflect.Message { mi := &file_replicationtypes_bertyreplication_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ReplicateGlobalStats_Request.ProtoReflect.Descriptor instead. func (*ReplicateGlobalStats_Request) Descriptor() ([]byte, []int) { return file_replicationtypes_bertyreplication_proto_rawDescGZIP(), []int{3, 0} } type ReplicateGlobalStats_Reply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields StartedAt int64 `protobuf:"varint,1,opt,name=started_at,json=startedAt,proto3" json:"started_at,omitempty"` ReplicatedGroups int64 `protobuf:"varint,2,opt,name=replicated_groups,json=replicatedGroups,proto3" json:"replicated_groups,omitempty"` TotalMetadataEntries int64 `protobuf:"varint,3,opt,name=total_metadata_entries,json=totalMetadataEntries,proto3" json:"total_metadata_entries,omitempty"` TotalMessageEntries int64 `protobuf:"varint,4,opt,name=total_message_entries,json=totalMessageEntries,proto3" json:"total_message_entries,omitempty"` } func (x *ReplicateGlobalStats_Reply) Reset() { *x = ReplicateGlobalStats_Reply{} if protoimpl.UnsafeEnabled { mi := &file_replicationtypes_bertyreplication_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ReplicateGlobalStats_Reply) String() string { return protoimpl.X.MessageStringOf(x) } func (*ReplicateGlobalStats_Reply) ProtoMessage() {} func (x *ReplicateGlobalStats_Reply) ProtoReflect() protoreflect.Message { mi := &file_replicationtypes_bertyreplication_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ReplicateGlobalStats_Reply.ProtoReflect.Descriptor instead. func (*ReplicateGlobalStats_Reply) Descriptor() ([]byte, []int) { return file_replicationtypes_bertyreplication_proto_rawDescGZIP(), []int{3, 1} } func (x *ReplicateGlobalStats_Reply) GetStartedAt() int64 { if x != nil { return x.StartedAt } return 0 } func (x *ReplicateGlobalStats_Reply) GetReplicatedGroups() int64 { if x != nil { return x.ReplicatedGroups } return 0 } func (x *ReplicateGlobalStats_Reply) GetTotalMetadataEntries() int64 { if x != nil { return x.TotalMetadataEntries } return 0 } func (x *ReplicateGlobalStats_Reply) GetTotalMessageEntries() int64 { if x != nil { return x.TotalMessageEntries } return 0 } type ReplicateGroupStats_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields GroupPublicKey string `protobuf:"bytes,1,opt,name=group_public_key,json=groupPublicKey,proto3" json:"group_public_key,omitempty"` } func (x *ReplicateGroupStats_Request) Reset() { *x = ReplicateGroupStats_Request{} if protoimpl.UnsafeEnabled { mi := &file_replicationtypes_bertyreplication_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ReplicateGroupStats_Request) String() string { return protoimpl.X.MessageStringOf(x) } func (*ReplicateGroupStats_Request) ProtoMessage() {} func (x *ReplicateGroupStats_Request) ProtoReflect() protoreflect.Message { mi := &file_replicationtypes_bertyreplication_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ReplicateGroupStats_Request.ProtoReflect.Descriptor instead. func (*ReplicateGroupStats_Request) Descriptor() ([]byte, []int) { return file_replicationtypes_bertyreplication_proto_rawDescGZIP(), []int{4, 0} } func (x *ReplicateGroupStats_Request) GetGroupPublicKey() string { if x != nil { return x.GroupPublicKey } return "" } type ReplicateGroupStats_Reply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Group *ReplicatedGroup `protobuf:"bytes,1,opt,name=group,proto3" json:"group,omitempty"` } func (x *ReplicateGroupStats_Reply) Reset() { *x = ReplicateGroupStats_Reply{} if protoimpl.UnsafeEnabled { mi := &file_replicationtypes_bertyreplication_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ReplicateGroupStats_Reply) String() string { return protoimpl.X.MessageStringOf(x) } func (*ReplicateGroupStats_Reply) ProtoMessage() {} func (x *ReplicateGroupStats_Reply) ProtoReflect() protoreflect.Message { mi := &file_replicationtypes_bertyreplication_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ReplicateGroupStats_Reply.ProtoReflect.Descriptor instead. func (*ReplicateGroupStats_Reply) Descriptor() ([]byte, []int) { return file_replicationtypes_bertyreplication_proto_rawDescGZIP(), []int{4, 1} } func (x *ReplicateGroupStats_Reply) GetGroup() *ReplicatedGroup { if x != nil { return x.Group } return nil } var File_replicationtypes_bertyreplication_proto protoreflect.FileDescriptor var file_replicationtypes_bertyreplication_proto_rawDesc = []byte{ 0x0a, 0x27, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x62, 0x65, 0x72, 0x74, 0x79, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x16, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x1a, 0x13, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x13, 0x74, 0x61, 0x67, 0x67, 0x65, 0x72, 0x2f, 0x74, 0x61, 0x67, 0x67, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x88, 0x03, 0x0a, 0x0f, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x35, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x16, 0x9a, 0x84, 0x9e, 0x03, 0x11, 0x67, 0x6f, 0x72, 0x6d, 0x3a, 0x22, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x4b, 0x65, 0x79, 0x22, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x69, 0x67, 0x6e, 0x5f, 0x70, 0x75, 0x62, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x69, 0x67, 0x6e, 0x50, 0x75, 0x62, 0x12, 0x19, 0x0a, 0x08, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6c, 0x69, 0x6e, 0x6b, 0x4b, 0x65, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x64, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x65, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x34, 0x0a, 0x16, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x66, 0x20, 0x01, 0x28, 0x03, 0x52, 0x14, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x30, 0x0a, 0x14, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x18, 0x67, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x4c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x48, 0x65, 0x61, 0x64, 0x12, 0x32, 0x0a, 0x15, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x68, 0x20, 0x01, 0x28, 0x03, 0x52, 0x13, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x18, 0x69, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x48, 0x65, 0x61, 0x64, 0x22, 0x90, 0x03, 0x0a, 0x14, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x6f, 0x0a, 0x1b, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x30, 0x9a, 0x84, 0x9e, 0x03, 0x2b, 0x67, 0x6f, 0x72, 0x6d, 0x3a, 0x22, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x3b, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x4b, 0x65, 0x79, 0x3b, 0x61, 0x75, 0x74, 0x6f, 0x49, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x22, 0x52, 0x18, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x52, 0x0a, 0x10, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x0f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x4d, 0x0a, 0x0c, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x69, 0x73, 0x73, 0x75, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x2a, 0x9a, 0x84, 0x9e, 0x03, 0x25, 0x67, 0x6f, 0x72, 0x6d, 0x3a, 0x22, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x4b, 0x65, 0x79, 0x3b, 0x61, 0x75, 0x74, 0x6f, 0x49, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x22, 0x52, 0x0b, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x49, 0x73, 0x73, 0x75, 0x65, 0x72, 0x12, 0x45, 0x0a, 0x08, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x2a, 0x9a, 0x84, 0x9e, 0x03, 0x25, 0x67, 0x6f, 0x72, 0x6d, 0x3a, 0x22, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x4b, 0x65, 0x79, 0x3b, 0x61, 0x75, 0x74, 0x6f, 0x49, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x22, 0x52, 0x07, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x22, 0x78, 0x0a, 0x20, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x1a, 0x3b, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x30, 0x0a, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x1a, 0x17, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x6f, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x02, 0x6f, 0x6b, 0x22, 0xe1, 0x01, 0x0a, 0x14, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x73, 0x1a, 0x09, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0xbd, 0x01, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x2b, 0x0a, 0x11, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x34, 0x0a, 0x16, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x14, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x13, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x22, 0x92, 0x01, 0x0a, 0x13, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x74, 0x61, 0x74, 0x73, 0x1a, 0x33, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x1a, 0x46, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x3d, 0x0a, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x32, 0xab, 0x03, 0x0a, 0x12, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x92, 0x01, 0x0a, 0x0e, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x40, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3e, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x80, 0x01, 0x0a, 0x14, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x34, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x7d, 0x0a, 0x13, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x33, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x42, 0x2c, 0x5a, 0x2a, 0x62, 0x65, 0x72, 0x74, 0x79, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x2f, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2f, 0x76, 0x32, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( file_replicationtypes_bertyreplication_proto_rawDescOnce sync.Once file_replicationtypes_bertyreplication_proto_rawDescData = file_replicationtypes_bertyreplication_proto_rawDesc ) func file_replicationtypes_bertyreplication_proto_rawDescGZIP() []byte { file_replicationtypes_bertyreplication_proto_rawDescOnce.Do(func() { file_replicationtypes_bertyreplication_proto_rawDescData = protoimpl.X.CompressGZIP(file_replicationtypes_bertyreplication_proto_rawDescData) }) return file_replicationtypes_bertyreplication_proto_rawDescData } var file_replicationtypes_bertyreplication_proto_msgTypes = make([]protoimpl.MessageInfo, 11) var file_replicationtypes_bertyreplication_proto_goTypes = []any{ (*ReplicatedGroup)(nil), // 0: weshnet.replication.v1.ReplicatedGroup (*ReplicatedGroupToken)(nil), // 1: weshnet.replication.v1.ReplicatedGroupToken (*ReplicationServiceReplicateGroup)(nil), // 2: weshnet.replication.v1.ReplicationServiceReplicateGroup (*ReplicateGlobalStats)(nil), // 3: weshnet.replication.v1.ReplicateGlobalStats (*ReplicateGroupStats)(nil), // 4: weshnet.replication.v1.ReplicateGroupStats (*ReplicationServiceReplicateGroup_Request)(nil), // 5: weshnet.replication.v1.ReplicationServiceReplicateGroup.Request (*ReplicationServiceReplicateGroup_Reply)(nil), // 6: weshnet.replication.v1.ReplicationServiceReplicateGroup.Reply (*ReplicateGlobalStats_Request)(nil), // 7: weshnet.replication.v1.ReplicateGlobalStats.Request (*ReplicateGlobalStats_Reply)(nil), // 8: weshnet.replication.v1.ReplicateGlobalStats.Reply (*ReplicateGroupStats_Request)(nil), // 9: weshnet.replication.v1.ReplicateGroupStats.Request (*ReplicateGroupStats_Reply)(nil), // 10: weshnet.replication.v1.ReplicateGroupStats.Reply (*protocoltypes.Group)(nil), // 11: weshnet.protocol.v1.Group } var file_replicationtypes_bertyreplication_proto_depIdxs = []int32{ 0, // 0: weshnet.replication.v1.ReplicatedGroupToken.replicated_group:type_name -> weshnet.replication.v1.ReplicatedGroup 11, // 1: weshnet.replication.v1.ReplicationServiceReplicateGroup.Request.group:type_name -> weshnet.protocol.v1.Group 0, // 2: weshnet.replication.v1.ReplicateGroupStats.Reply.group:type_name -> weshnet.replication.v1.ReplicatedGroup 5, // 3: weshnet.replication.v1.ReplicationService.ReplicateGroup:input_type -> weshnet.replication.v1.ReplicationServiceReplicateGroup.Request 7, // 4: weshnet.replication.v1.ReplicationService.ReplicateGlobalStats:input_type -> weshnet.replication.v1.ReplicateGlobalStats.Request 9, // 5: weshnet.replication.v1.ReplicationService.ReplicateGroupStats:input_type -> weshnet.replication.v1.ReplicateGroupStats.Request 6, // 6: weshnet.replication.v1.ReplicationService.ReplicateGroup:output_type -> weshnet.replication.v1.ReplicationServiceReplicateGroup.Reply 8, // 7: weshnet.replication.v1.ReplicationService.ReplicateGlobalStats:output_type -> weshnet.replication.v1.ReplicateGlobalStats.Reply 10, // 8: weshnet.replication.v1.ReplicationService.ReplicateGroupStats:output_type -> weshnet.replication.v1.ReplicateGroupStats.Reply 6, // [6:9] is the sub-list for method output_type 3, // [3:6] is the sub-list for method input_type 3, // [3:3] is the sub-list for extension type_name 3, // [3:3] is the sub-list for extension extendee 0, // [0:3] is the sub-list for field type_name } func init() { file_replicationtypes_bertyreplication_proto_init() } func file_replicationtypes_bertyreplication_proto_init() { if File_replicationtypes_bertyreplication_proto != nil { return } if !protoimpl.UnsafeEnabled { file_replicationtypes_bertyreplication_proto_msgTypes[0].Exporter = func(v any, i int) any { switch v := v.(*ReplicatedGroup); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_replicationtypes_bertyreplication_proto_msgTypes[1].Exporter = func(v any, i int) any { switch v := v.(*ReplicatedGroupToken); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_replicationtypes_bertyreplication_proto_msgTypes[2].Exporter = func(v any, i int) any { switch v := v.(*ReplicationServiceReplicateGroup); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_replicationtypes_bertyreplication_proto_msgTypes[3].Exporter = func(v any, i int) any { switch v := v.(*ReplicateGlobalStats); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_replicationtypes_bertyreplication_proto_msgTypes[4].Exporter = func(v any, i int) any { switch v := v.(*ReplicateGroupStats); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_replicationtypes_bertyreplication_proto_msgTypes[5].Exporter = func(v any, i int) any { switch v := v.(*ReplicationServiceReplicateGroup_Request); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_replicationtypes_bertyreplication_proto_msgTypes[6].Exporter = func(v any, i int) any { switch v := v.(*ReplicationServiceReplicateGroup_Reply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_replicationtypes_bertyreplication_proto_msgTypes[7].Exporter = func(v any, i int) any { switch v := v.(*ReplicateGlobalStats_Request); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_replicationtypes_bertyreplication_proto_msgTypes[8].Exporter = func(v any, i int) any { switch v := v.(*ReplicateGlobalStats_Reply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_replicationtypes_bertyreplication_proto_msgTypes[9].Exporter = func(v any, i int) any { switch v := v.(*ReplicateGroupStats_Request); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_replicationtypes_bertyreplication_proto_msgTypes[10].Exporter = func(v any, i int) any { switch v := v.(*ReplicateGroupStats_Reply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_replicationtypes_bertyreplication_proto_rawDesc, NumEnums: 0, NumMessages: 11, NumExtensions: 0, NumServices: 1, }, GoTypes: file_replicationtypes_bertyreplication_proto_goTypes, DependencyIndexes: file_replicationtypes_bertyreplication_proto_depIdxs, MessageInfos: file_replicationtypes_bertyreplication_proto_msgTypes, }.Build() File_replicationtypes_bertyreplication_proto = out.File file_replicationtypes_bertyreplication_proto_rawDesc = nil file_replicationtypes_bertyreplication_proto_goTypes = nil file_replicationtypes_bertyreplication_proto_depIdxs = nil } ================================================ FILE: pkg/replicationtypes/bertyreplication.pb.gw.go ================================================ // Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. // source: replicationtypes/bertyreplication.proto /* Package replicationtypes is a reverse proxy. It translates gRPC into RESTful JSON APIs. */ package replicationtypes import ( "context" "io" "net/http" "github.com/golang/protobuf/descriptor" "github.com/golang/protobuf/proto" "github.com/grpc-ecosystem/grpc-gateway/runtime" "github.com/grpc-ecosystem/grpc-gateway/utilities" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/grpclog" "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" ) // Suppress "imported and not used" errors var _ codes.Code var _ io.Reader var _ status.Status var _ = runtime.String var _ = utilities.NewDoubleArray var _ = descriptor.ForMessage var _ = metadata.Join func request_ReplicationService_ReplicateGroup_0(ctx context.Context, marshaler runtime.Marshaler, client ReplicationServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq ReplicationServiceReplicateGroup_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := client.ReplicateGroup(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } func 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) { var protoReq ReplicationServiceReplicateGroup_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := server.ReplicateGroup(ctx, &protoReq) return msg, metadata, err } func request_ReplicationService_ReplicateGlobalStats_0(ctx context.Context, marshaler runtime.Marshaler, client ReplicationServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq ReplicateGlobalStats_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := client.ReplicateGlobalStats(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } func 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) { var protoReq ReplicateGlobalStats_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := server.ReplicateGlobalStats(ctx, &protoReq) return msg, metadata, err } func request_ReplicationService_ReplicateGroupStats_0(ctx context.Context, marshaler runtime.Marshaler, client ReplicationServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq ReplicateGroupStats_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := client.ReplicateGroupStats(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } func 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) { var protoReq ReplicateGroupStats_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := server.ReplicateGroupStats(ctx, &protoReq) return msg, metadata, err } // RegisterReplicationServiceHandlerServer registers the http handlers for service ReplicationService to "mux". // UnaryRPC :call ReplicationServiceServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. // Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterReplicationServiceHandlerFromEndpoint instead. func RegisterReplicationServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server ReplicationServiceServer) error { mux.Handle("POST", pattern_ReplicationService_ReplicateGroup_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := local_request_ReplicationService_ReplicateGroup_0(rctx, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ReplicationService_ReplicateGroup_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ReplicationService_ReplicateGlobalStats_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := local_request_ReplicationService_ReplicateGlobalStats_0(rctx, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ReplicationService_ReplicateGlobalStats_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ReplicationService_ReplicateGroupStats_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := local_request_ReplicationService_ReplicateGroupStats_0(rctx, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ReplicationService_ReplicateGroupStats_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) return nil } // RegisterReplicationServiceHandlerFromEndpoint is same as RegisterReplicationServiceHandler but // automatically dials to "endpoint" and closes the connection when "ctx" gets done. func RegisterReplicationServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { conn, err := grpc.Dial(endpoint, opts...) if err != nil { return err } defer func() { if err != nil { if cerr := conn.Close(); cerr != nil { grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) } return } go func() { <-ctx.Done() if cerr := conn.Close(); cerr != nil { grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) } }() }() return RegisterReplicationServiceHandler(ctx, mux, conn) } // RegisterReplicationServiceHandler registers the http handlers for service ReplicationService to "mux". // The handlers forward requests to the grpc endpoint over "conn". func RegisterReplicationServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { return RegisterReplicationServiceHandlerClient(ctx, mux, NewReplicationServiceClient(conn)) } // RegisterReplicationServiceHandlerClient registers the http handlers for service ReplicationService // to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "ReplicationServiceClient". // Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "ReplicationServiceClient" // doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in // "ReplicationServiceClient" to call the correct interceptors. func RegisterReplicationServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client ReplicationServiceClient) error { mux.Handle("POST", pattern_ReplicationService_ReplicateGroup_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_ReplicationService_ReplicateGroup_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ReplicationService_ReplicateGroup_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ReplicationService_ReplicateGlobalStats_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_ReplicationService_ReplicateGlobalStats_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ReplicationService_ReplicateGlobalStats_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_ReplicationService_ReplicateGroupStats_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_ReplicationService_ReplicateGroupStats_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_ReplicationService_ReplicateGroupStats_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) return nil } var ( pattern_ReplicationService_ReplicateGroup_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.replication.v1.ReplicationService", "ReplicateGroup"}, "", runtime.AssumeColonVerbOpt(true))) pattern_ReplicationService_ReplicateGlobalStats_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.replication.v1.ReplicationService", "ReplicateGlobalStats"}, "", runtime.AssumeColonVerbOpt(true))) pattern_ReplicationService_ReplicateGroupStats_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.replication.v1.ReplicationService", "ReplicateGroupStats"}, "", runtime.AssumeColonVerbOpt(true))) ) var ( forward_ReplicationService_ReplicateGroup_0 = runtime.ForwardResponseMessage forward_ReplicationService_ReplicateGlobalStats_0 = runtime.ForwardResponseMessage forward_ReplicationService_ReplicateGroupStats_0 = runtime.ForwardResponseMessage ) ================================================ FILE: pkg/replicationtypes/bertyreplication_grpc.pb.go ================================================ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.5.1 // - protoc (unknown) // source: replicationtypes/bertyreplication.proto package replicationtypes import ( context "context" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" ) // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. // Requires gRPC-Go v1.64.0 or later. const _ = grpc.SupportPackageIsVersion9 const ( ReplicationService_ReplicateGroup_FullMethodName = "/weshnet.replication.v1.ReplicationService/ReplicateGroup" ReplicationService_ReplicateGlobalStats_FullMethodName = "/weshnet.replication.v1.ReplicationService/ReplicateGlobalStats" ReplicationService_ReplicateGroupStats_FullMethodName = "/weshnet.replication.v1.ReplicationService/ReplicateGroupStats" ) // ReplicationServiceClient is the client API for ReplicationService service. // // 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. // // ReplicationService type ReplicationServiceClient interface { // ReplicateGroup ReplicateGroup(ctx context.Context, in *ReplicationServiceReplicateGroup_Request, opts ...grpc.CallOption) (*ReplicationServiceReplicateGroup_Reply, error) ReplicateGlobalStats(ctx context.Context, in *ReplicateGlobalStats_Request, opts ...grpc.CallOption) (*ReplicateGlobalStats_Reply, error) ReplicateGroupStats(ctx context.Context, in *ReplicateGroupStats_Request, opts ...grpc.CallOption) (*ReplicateGroupStats_Reply, error) } type replicationServiceClient struct { cc grpc.ClientConnInterface } func NewReplicationServiceClient(cc grpc.ClientConnInterface) ReplicationServiceClient { return &replicationServiceClient{cc} } func (c *replicationServiceClient) ReplicateGroup(ctx context.Context, in *ReplicationServiceReplicateGroup_Request, opts ...grpc.CallOption) (*ReplicationServiceReplicateGroup_Reply, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ReplicationServiceReplicateGroup_Reply) err := c.cc.Invoke(ctx, ReplicationService_ReplicateGroup_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *replicationServiceClient) ReplicateGlobalStats(ctx context.Context, in *ReplicateGlobalStats_Request, opts ...grpc.CallOption) (*ReplicateGlobalStats_Reply, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ReplicateGlobalStats_Reply) err := c.cc.Invoke(ctx, ReplicationService_ReplicateGlobalStats_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *replicationServiceClient) ReplicateGroupStats(ctx context.Context, in *ReplicateGroupStats_Request, opts ...grpc.CallOption) (*ReplicateGroupStats_Reply, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ReplicateGroupStats_Reply) err := c.cc.Invoke(ctx, ReplicationService_ReplicateGroupStats_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } // ReplicationServiceServer is the server API for ReplicationService service. // All implementations must embed UnimplementedReplicationServiceServer // for forward compatibility. // // ReplicationService type ReplicationServiceServer interface { // ReplicateGroup ReplicateGroup(context.Context, *ReplicationServiceReplicateGroup_Request) (*ReplicationServiceReplicateGroup_Reply, error) ReplicateGlobalStats(context.Context, *ReplicateGlobalStats_Request) (*ReplicateGlobalStats_Reply, error) ReplicateGroupStats(context.Context, *ReplicateGroupStats_Request) (*ReplicateGroupStats_Reply, error) mustEmbedUnimplementedReplicationServiceServer() } // UnimplementedReplicationServiceServer must be embedded to have // forward compatible implementations. // // NOTE: this should be embedded by value instead of pointer to avoid a nil // pointer dereference when methods are called. type UnimplementedReplicationServiceServer struct{} func (UnimplementedReplicationServiceServer) ReplicateGroup(context.Context, *ReplicationServiceReplicateGroup_Request) (*ReplicationServiceReplicateGroup_Reply, error) { return nil, status.Errorf(codes.Unimplemented, "method ReplicateGroup not implemented") } func (UnimplementedReplicationServiceServer) ReplicateGlobalStats(context.Context, *ReplicateGlobalStats_Request) (*ReplicateGlobalStats_Reply, error) { return nil, status.Errorf(codes.Unimplemented, "method ReplicateGlobalStats not implemented") } func (UnimplementedReplicationServiceServer) ReplicateGroupStats(context.Context, *ReplicateGroupStats_Request) (*ReplicateGroupStats_Reply, error) { return nil, status.Errorf(codes.Unimplemented, "method ReplicateGroupStats not implemented") } func (UnimplementedReplicationServiceServer) mustEmbedUnimplementedReplicationServiceServer() {} func (UnimplementedReplicationServiceServer) testEmbeddedByValue() {} // UnsafeReplicationServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to ReplicationServiceServer will // result in compilation errors. type UnsafeReplicationServiceServer interface { mustEmbedUnimplementedReplicationServiceServer() } func RegisterReplicationServiceServer(s grpc.ServiceRegistrar, srv ReplicationServiceServer) { // If the following call pancis, it indicates UnimplementedReplicationServiceServer was // embedded by pointer and is nil. This will cause panics if an // unimplemented method is ever invoked, so we test this at initialization // time to prevent it from happening at runtime later due to I/O. if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { t.testEmbeddedByValue() } s.RegisterService(&ReplicationService_ServiceDesc, srv) } func _ReplicationService_ReplicateGroup_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ReplicationServiceReplicateGroup_Request) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(ReplicationServiceServer).ReplicateGroup(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: ReplicationService_ReplicateGroup_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ReplicationServiceServer).ReplicateGroup(ctx, req.(*ReplicationServiceReplicateGroup_Request)) } return interceptor(ctx, in, info, handler) } func _ReplicationService_ReplicateGlobalStats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ReplicateGlobalStats_Request) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(ReplicationServiceServer).ReplicateGlobalStats(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: ReplicationService_ReplicateGlobalStats_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ReplicationServiceServer).ReplicateGlobalStats(ctx, req.(*ReplicateGlobalStats_Request)) } return interceptor(ctx, in, info, handler) } func _ReplicationService_ReplicateGroupStats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ReplicateGroupStats_Request) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(ReplicationServiceServer).ReplicateGroupStats(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: ReplicationService_ReplicateGroupStats_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ReplicationServiceServer).ReplicateGroupStats(ctx, req.(*ReplicateGroupStats_Request)) } return interceptor(ctx, in, info, handler) } // ReplicationService_ServiceDesc is the grpc.ServiceDesc for ReplicationService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var ReplicationService_ServiceDesc = grpc.ServiceDesc{ ServiceName: "weshnet.replication.v1.ReplicationService", HandlerType: (*ReplicationServiceServer)(nil), Methods: []grpc.MethodDesc{ { MethodName: "ReplicateGroup", Handler: _ReplicationService_ReplicateGroup_Handler, }, { MethodName: "ReplicateGlobalStats", Handler: _ReplicationService_ReplicateGlobalStats_Handler, }, { MethodName: "ReplicateGroupStats", Handler: _ReplicationService_ReplicateGroupStats_Handler, }, }, Streams: []grpc.StreamDesc{}, Metadata: "replicationtypes/bertyreplication.proto", } ================================================ FILE: pkg/replicationtypes/consts.go ================================================ package replicationtypes const ( ServiceReplicationRegisteredPrefix = "user_registration" ServiceReplicationKeyGroupPrefix = "group" ) ================================================ FILE: pkg/replicationtypes/models.go ================================================ package replicationtypes import ( "encoding/base64" "berty.tech/weshnet/v2/pkg/protocoltypes" ) func (m *ReplicatedGroup) ToGroup() (*protocoltypes.Group, error) { pk, err := base64.RawURLEncoding.DecodeString(m.PublicKey) if err != nil { return nil, err } signPub, err := base64.RawURLEncoding.DecodeString(m.SignPub) if err != nil { return nil, err } linkKey, err := base64.RawURLEncoding.DecodeString(m.LinkKey) if err != nil { return nil, err } return &protocoltypes.Group{ PublicKey: pk, SignPub: signPub, LinkKey: linkKey, }, nil } ================================================ FILE: pkg/secretstore/chain_key.go ================================================ package secretstore import ( crand "crypto/rand" "fmt" "github.com/libp2p/go-libp2p/core/crypto" "golang.org/x/crypto/nacl/box" "google.golang.org/protobuf/proto" "berty.tech/weshnet/v2/pkg/cryptoutil" "berty.tech/weshnet/v2/pkg/errcode" "berty.tech/weshnet/v2/pkg/protocoltypes" ) // newDeviceChainKey creates a new random chain key func newDeviceChainKey() (*protocoltypes.DeviceChainKey, error) { chainKey := make([]byte, 32) _, err := crand.Read(chainKey) if err != nil { return nil, errcode.ErrCode_ErrCryptoRandomGeneration.Wrap(err) } return &protocoltypes.DeviceChainKey{ ChainKey: chainKey, Counter: 0, }, nil } // encryptDeviceChainKey encrypts a device chain key for a target member func encryptDeviceChainKey(localDevicePrivateKey crypto.PrivKey, remoteMemberPubKey crypto.PubKey, deviceChainKey *protocoltypes.DeviceChainKey, group *protocoltypes.Group) ([]byte, error) { chainKeyBytes, err := proto.Marshal(deviceChainKey) if err != nil { return nil, errcode.ErrCode_ErrSerialization.Wrap(err) } mongPriv, mongPub, err := cryptoutil.EdwardsToMontgomery(localDevicePrivateKey, remoteMemberPubKey) if err != nil { return nil, errcode.ErrCode_ErrCryptoKeyConversion.Wrap(err) } nonce := groupIDToNonce(group) encryptedChainKey := box.Seal(nil, chainKeyBytes, nonce, mongPub, mongPriv) return encryptedChainKey, nil } // decryptDeviceChainKey decrypts a chain key sent by the given device func decryptDeviceChainKey(encryptedDeviceChainKey []byte, group *protocoltypes.Group, localMemberPrivateKey crypto.PrivKey, senderDevicePubKey crypto.PubKey) (*protocoltypes.DeviceChainKey, error) { mongPriv, mongPub, err := cryptoutil.EdwardsToMontgomery(localMemberPrivateKey, senderDevicePubKey) if err != nil { return nil, errcode.ErrCode_ErrCryptoKeyConversion.Wrap(err) } nonce := groupIDToNonce(group) decryptedSecret := &protocoltypes.DeviceChainKey{} decryptedMessage, ok := box.Open(nil, encryptedDeviceChainKey, nonce, mongPub, mongPriv) if !ok { return nil, errcode.ErrCode_ErrCryptoDecrypt.Wrap(fmt.Errorf("unable to decrypt message")) } err = proto.Unmarshal(decryptedMessage, decryptedSecret) if err != nil { return nil, errcode.ErrCode_ErrDeserialization.Wrap(err) } return decryptedSecret, nil } // groupIDToNonce converts a group public key to a value which can be used as // a nonce of the nacl library func groupIDToNonce(group *protocoltypes.Group) *[cryptoutil.NonceSize]byte { // Nonce doesn't need to be secret, random nor unpredictable, it just needs // to be used only once for a given sender+receiver set, and we will send // only one SecretEntryPayload per localDevicePrivateKey+remoteMemberPubKey // So we can reuse groupID as nonce for all SecretEntryPayload and save // 24 bytes of storage and bandwidth for each of them. // // See https://pynacl.readthedocs.io/en/stable/secret/#nonce // See Security Model here: https://nacl.cr.yp.to/box.html var nonce [cryptoutil.NonceSize]byte gid := group.GetPublicKey() copy(nonce[:], gid) return &nonce } ================================================ FILE: pkg/secretstore/datastore_keys.go ================================================ package secretstore import ( "encoding/base64" "encoding/hex" "fmt" "github.com/ipfs/go-cid" "github.com/ipfs/go-datastore" "github.com/libp2p/go-libp2p/core/crypto" "berty.tech/weshnet/v2/pkg/errcode" ) const ( // dsNamespaceChainKeyForDeviceOnGroup is a namespace stores the current // state of a device chain key for a given group. // It contains the secret used to derive the next value of the chain key // and used to generate a message key for the message at `counter` value, // then put in the dsNamespacePrecomputedMessageKeys namespace. dsNamespaceChainKeyForDeviceOnGroup = "chainKeyForDeviceOnGroup" // dsNamespacePrecomputedMessageKeys is a namespace storing precomputed // message keys for a given group, device and message counter. // As the chain key stored has already been derived, these message keys // need to be computed beforehand. // The corresponding message can then be decrypted via a quick lookup. dsNamespacePrecomputedMessageKeys = "precomputedMessageKeys" // dsNamespaceMessageKeyForCIDs is a namespace containing the message key // for a given CID once the corresponding message has been decrypted. dsNamespaceMessageKeyForCIDs = "messageKeyForCIDs" // dsNamespaceOutOfStoreGroupHint is a namespace where HMAC value are // associated to a group public key. // It is used when receiving an out-of-store message (e.g. a push // notification) to identify the group on which the message belongs, which // can then be decrypted. dsNamespaceOutOfStoreGroupHint = "outOfStoreGroupHint" // dsNamespaceOutOfStoreGroupHintCounters is a namespace storing first and // last counter values for generated group hints inside the // dsNamespaceOutOfStoreGroupHint namespace dsNamespaceOutOfStoreGroupHintCounters = "outOfStoreGroupHintCounters" // dsNamespaceGroupDatastore is a namespace to store groups by their public // key dsNamespaceGroupDatastore = "groupByPublicKey" ) func dsKeyForGroup(key []byte) datastore.Key { return datastore.KeyWithNamespaces([]string{ dsNamespaceGroupDatastore, base64.RawURLEncoding.EncodeToString(key), }) } // dsKeyForPrecomputedMessageKey returns a datastore.Key where will be stored a // precalculated message key for a given group and device func dsKeyForPrecomputedMessageKey(groupPublicKey, devicePublicKey []byte, counter uint64) datastore.Key { return datastore.KeyWithNamespaces([]string{ dsNamespacePrecomputedMessageKeys, hex.EncodeToString(groupPublicKey), hex.EncodeToString(devicePublicKey), fmt.Sprintf("%d", counter), }) } // dsKeyForCurrentChainKey returns a datastore.Key where will be stored a // device chain key for a given group. func dsKeyForCurrentChainKey(groupPublicKey crypto.PubKey, devicePublicKey crypto.PubKey) (datastore.Key, error) { devicePublicKeyBytes, err := devicePublicKey.Raw() if err != nil { return datastore.Key{}, errcode.ErrCode_ErrSerialization.Wrap(err) } groupPublicKeyBytes, err := groupPublicKey.Raw() if err != nil { return datastore.Key{}, errcode.ErrCode_ErrSerialization.Wrap(err) } return datastore.KeyWithNamespaces([]string{ dsNamespaceChainKeyForDeviceOnGroup, hex.EncodeToString(groupPublicKeyBytes), hex.EncodeToString(devicePublicKeyBytes), }), nil } // dsKeyForMessageKeyByCID returns a datastore.Key where will be stored a // message decryption key for a given message CID. func dsKeyForMessageKeyByCID(id cid.Cid) datastore.Key { // TODO: specify the id return datastore.KeyWithNamespaces([]string{ dsNamespaceMessageKeyForCIDs, id.String(), }) } // dsKeyForOutOfStoreMessageGroupHint returns a datastore.Key where will be // stored a group public key for a given push group reference. func dsKeyForOutOfStoreMessageGroupHint(ref []byte) datastore.Key { return datastore.KeyWithNamespaces([]string{ dsNamespaceOutOfStoreGroupHint, base64.RawURLEncoding.EncodeToString(ref), }) } // dsKeyForOutOfStoreFirstLastCounters returns the datastore.Key where will be // stored a protocoltypes.FirstLastCounters struct for the given group public // key and device public key. func dsKeyForOutOfStoreFirstLastCounters(groupPK, devicePK []byte) datastore.Key { return datastore.KeyWithNamespaces([]string{ dsNamespaceOutOfStoreGroupHintCounters, base64.RawURLEncoding.EncodeToString(groupPK), base64.RawURLEncoding.EncodeToString(devicePK), }) } ================================================ FILE: pkg/secretstore/device_keystore_wrapper.go ================================================ package secretstore import ( "crypto/ed25519" crand "crypto/rand" "encoding/hex" "fmt" "strings" "sync" "github.com/aead/ecdh" keystore "github.com/ipfs/go-ipfs-keystore" "github.com/libp2p/go-libp2p/core/crypto" "go.uber.org/zap" "berty.tech/weshnet/v2/pkg/cryptoutil" "berty.tech/weshnet/v2/pkg/errcode" "berty.tech/weshnet/v2/pkg/protocoltypes" ) const ( keyAccount = "accountSK" keyAccountProof = "accountProofSK" keyDevice = "deviceSK" keyMemberDevice = "memberDeviceSK" keyMember = "memberSK" keyContactGroup = "contactGroupSK" ) // deviceKeystore is a wrapper around a keystore.Keystore object. // It contains methods to manipulate member and device keys. type deviceKeystore struct { keystore keystore.Keystore mu sync.Mutex logger *zap.Logger } // newDeviceKeystore instantiate a new device keystore func newDeviceKeystore(ks keystore.Keystore, logger *zap.Logger) *deviceKeystore { if logger == nil { logger = zap.NewNop() } return &deviceKeystore{ keystore: ks, logger: logger, } } // getAccountPrivateKey returns the private key of the current account func (a *deviceKeystore) getAccountPrivateKey() (crypto.PrivKey, error) { a.mu.Lock() defer a.mu.Unlock() return a.getOrGenerateNamedKey(keyAccount) } // getAccountProofPrivateKey returns the private proof key of // the current account func (a *deviceKeystore) getAccountProofPrivateKey() (crypto.PrivKey, error) { a.mu.Lock() defer a.mu.Unlock() return a.getOrGenerateNamedKey(keyAccountProof) } // devicePrivateKey returns the current private key of the current device for // the account and one-to-one conversations func (a *deviceKeystore) devicePrivateKey() (crypto.PrivKey, error) { a.mu.Lock() defer a.mu.Unlock() return a.getOrGenerateNamedKey(keyDevice) } // contactGroupPrivateKey retrieves the key for the contact group // shared with the supplied contact's public key, this key will be derived to // form the contact group keys func (a *deviceKeystore) contactGroupPrivateKey(contactPublicKey crypto.PubKey) (crypto.PrivKey, error) { accountPrivateKey, err := a.getAccountPrivateKey() if err != nil { return nil, errcode.ErrCode_ErrInternal.Wrap(err) } return a.getOrComputeECDH(keyContactGroup, contactPublicKey, accountPrivateKey) } // memberDeviceForMultiMemberGroup retrieves the device private key for the // supplied group func (a *deviceKeystore) memberDeviceForMultiMemberGroup(groupPublicKey crypto.PubKey) (*ownMemberDevice, error) { memberPrivateKey, err := a.computeMemberKeyForMultiMemberGroup(groupPublicKey) if err != nil { return nil, errcode.ErrCode_ErrInternal.Wrap(fmt.Errorf("unable to get or generate a device key for group member: %w", err)) } devicePrivateKey, err := a.getOrGenerateDeviceKeyForMultiMemberGroup(groupPublicKey) if err != nil { return nil, errcode.ErrCode_ErrInternal.Wrap(err) } return newOwnMemberDevice(memberPrivateKey, devicePrivateKey), nil } // memberDeviceForGroup computes or retrieves the member and device key for the // supplied group func (a *deviceKeystore) memberDeviceForGroup(group *protocoltypes.Group) (*ownMemberDevice, error) { publicKey, err := group.GetPubKey() if err != nil { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("unable to get public key for group: %w", err)) } switch group.GetGroupType() { case protocoltypes.GroupType_GroupTypeAccount, protocoltypes.GroupType_GroupTypeContact: memberPrivateKey, err := a.getAccountPrivateKey() if err != nil { return nil, errcode.ErrCode_ErrInternal.Wrap(err) } devicePrivateKey, err := a.devicePrivateKey() if err != nil { return nil, errcode.ErrCode_ErrInternal.Wrap(err) } return newOwnMemberDevice(memberPrivateKey, devicePrivateKey), nil case protocoltypes.GroupType_GroupTypeMultiMember: return a.memberDeviceForMultiMemberGroup(publicKey) } return nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("unknown group type")) } // getOrGenerateNamedKey retrieves a private key by its name, or generate it // if missing func (a *deviceKeystore) getOrGenerateNamedKey(name string) (crypto.PrivKey, error) { privateKey, err := a.keystore.Get(name) if err == nil { return privateKey, nil } else if err.Error() != keystore.ErrNoSuchKey.Error() { return nil, errcode.ErrCode_ErrDBRead.Wrap(fmt.Errorf("unable to perform get operation on keystore: %w", err)) } privateKey, _, err = crypto.GenerateEd25519Key(crand.Reader) if err != nil { return nil, errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(fmt.Errorf("unable to generate an ed25519 key: %w", err)) } if err := a.keystore.Put(name, privateKey); err != nil { return nil, errcode.ErrCode_ErrDBWrite.Wrap(fmt.Errorf("unable to perform put operation on keystore: %w", err)) } return privateKey, nil } // getOrGenerateDeviceKeyForMultiMemberGroup fetches or generate a new device // key for a multi-member group. The results do not need to be deterministic // as it will only be used on the current device. func (a *deviceKeystore) getOrGenerateDeviceKeyForMultiMemberGroup(groupPublicKey crypto.PubKey) (crypto.PrivKey, error) { a.mu.Lock() defer a.mu.Unlock() groupPublicKeyRaw, err := groupPublicKey.Raw() if err != nil { return nil, errcode.ErrCode_ErrSerialization.Wrap(err) } name := strings.Join([]string{keyMemberDevice, hex.EncodeToString(groupPublicKeyRaw)}, "_") return a.getOrGenerateNamedKey(name) } // getOrComputeECDH fetches a named private key or computes one via an // elliptic-curve Diffie-Hellman key agreement if not cached func (a *deviceKeystore) getOrComputeECDH(nameSpace string, publicKey crypto.PubKey, ownPrivateKey crypto.PrivKey) (crypto.PrivKey, error) { a.mu.Lock() defer a.mu.Unlock() publicKeyRaw, err := publicKey.Raw() if err != nil { return nil, errcode.ErrCode_ErrSerialization.Wrap(err) } name := strings.Join([]string{nameSpace, hex.EncodeToString(publicKeyRaw)}, "_") privateKey, err := a.keystore.Get(name) if err == nil { return privateKey, nil } else if err.Error() != keystore.ErrNoSuchKey.Error() { return nil, errcode.ErrCode_ErrDBRead.Wrap(fmt.Errorf("unable to perform get operation on keystore: %w", err)) } privateKeyBytes, publicKeyBytes, err := cryptoutil.EdwardsToMontgomery(ownPrivateKey, publicKey) if err != nil { return nil, errcode.ErrCode_ErrCryptoKeyConversion.Wrap(err) } secret := ecdh.X25519().ComputeSecret(privateKeyBytes, publicKeyBytes) groupSecretPrivateKey := ed25519.NewKeyFromSeed(secret) privateKey, _, err = crypto.KeyPairFromStdKey(&groupSecretPrivateKey) if err != nil { return nil, errcode.ErrCode_ErrCryptoKeyConversion.Wrap(err) } if err := a.keystore.Put(name, privateKey); err != nil { return nil, errcode.ErrCode_ErrDBWrite.Wrap(err) } return privateKey, nil } // computeMemberKeyForMultiMemberGroup returns a deterministic private key // for a multi member group, this allows a group to be joined from two // different devices simultaneously without requiring a consensus. func (a *deviceKeystore) computeMemberKeyForMultiMemberGroup(groupPublicKey crypto.PubKey) (crypto.PrivKey, error) { accountProofPrivateKey, err := a.getAccountProofPrivateKey() if err != nil { return nil, errcode.ErrCode_ErrInternal.Wrap(err) } return a.getOrComputeECDH(keyMember, groupPublicKey, accountProofPrivateKey) } // restoreAccountKeys restores exported LibP2P keys into the deviceKeystore, it // will fail if accounts keys are already created or imported into the keystore func (a *deviceKeystore) restoreAccountKeys(accountPrivateKeyBytes []byte, accountProofPrivateKeyBytes []byte) error { privateKeys := map[string]crypto.PrivKey{} for keyName, keyBytes := range map[string][]byte{ keyAccount: accountPrivateKeyBytes, keyAccountProof: accountProofPrivateKeyBytes, } { var err error privateKeys[keyName], err = getEd25519PrivateKeyFromLibP2PFormattedBytes(keyBytes) if err != nil { return errcode.ErrCode_ErrDeserialization.Wrap(err) } } if privateKeys[keyAccount].Equals(privateKeys[keyAccountProof]) { return errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("the account key cannot be the same value as the account proof key")) } for keyName := range privateKeys { if exists, err := a.keystore.Has(keyName); err != nil { return errcode.ErrCode_ErrDBRead.Wrap(err) } else if exists { return errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("an account is already set in this keystore")) } } for keyName, privateKey := range privateKeys { if err := a.keystore.Put(keyName, privateKey); err != nil { return errcode.ErrCode_ErrDBWrite.Wrap(err) } } return nil } ================================================ FILE: pkg/secretstore/device_keystore_wrapper_test.go ================================================ package secretstore_test import ( crand "crypto/rand" "testing" "github.com/libp2p/go-libp2p/core/crypto" "github.com/stretchr/testify/assert" "berty.tech/weshnet/v2/pkg/protocoltypes" "berty.tech/weshnet/v2/pkg/secretstore" ) func Test_New_AccountPrivKey_AccountProofPrivKey(t *testing.T) { acc, err := secretstore.NewInMemSecretStore(nil) assert.NoError(t, err) assert.NotNil(t, acc) sk1, skProof1, err := acc.ExportAccountKeysForBackup() assert.NoError(t, err) assert.NotNil(t, sk1) assert.NotNil(t, skProof1) sk2, skProof2, err := acc.ExportAccountKeysForBackup() assert.NoError(t, err) assert.NotNil(t, sk2) assert.NotNil(t, skProof2) assert.Equal(t, sk1, sk2) assert.Equal(t, skProof1, skProof2) assert.NotEqual(t, sk1, skProof1) assert.NotEqual(t, sk1, skProof2) assert.NotEqual(t, sk2, skProof1) assert.NotEqual(t, sk2, skProof2) } func Test_ExportAccountKeys_ImportAccountKeys(t *testing.T) { acc1, err := secretstore.NewInMemSecretStore(nil) assert.NoError(t, err) assert.NotNil(t, acc1) sk1, skProof1, err := acc1.ExportAccountKeysForBackup() assert.NoError(t, err) assert.NotNil(t, sk1) assert.NotNil(t, skProof1) acc2, err := secretstore.NewInMemSecretStore(nil) assert.NoError(t, err) assert.NotNil(t, acc2) // Testing with a nil value { assert.Error(t, acc2.ImportAccountKeys(nil, skProof1)) assert.Error(t, acc2.ImportAccountKeys(sk1, nil)) } // Testing with an unsupported key format { invalidPriv, _, err := crypto.GenerateSecp256k1Key(crand.Reader) assert.NoError(t, err) invalidPrivBytes, err := crypto.MarshalPrivateKey(invalidPriv) assert.NoError(t, err) assert.Error(t, acc2.ImportAccountKeys(sk1, invalidPrivBytes)) assert.Error(t, acc2.ImportAccountKeys(invalidPrivBytes, skProof1)) } // Testing with an invalid key format { garbageBytes := []byte("garbage") assert.Error(t, acc2.ImportAccountKeys(sk1, garbageBytes)) assert.Error(t, acc2.ImportAccountKeys(garbageBytes, skProof1)) } // Testing with account and proof key being the same { assert.Error(t, acc2.ImportAccountKeys(sk1, sk1)) } // Valid test case { assert.NoError(t, acc2.ImportAccountKeys(sk1, skProof1)) } // Attempting to import keys again { assert.Error(t, acc2.ImportAccountKeys(sk1, skProof1)) } sk2, skProof2, err := acc1.ExportAccountKeysForBackup() assert.NoError(t, err) assert.NotNil(t, sk2) assert.NotNil(t, skProof2) assert.Equal(t, sk1, sk2) assert.Equal(t, skProof1, skProof2) assert.NotEqual(t, sk1, skProof1) assert.NotEqual(t, sk1, skProof2) assert.NotEqual(t, sk2, skProof1) assert.NotEqual(t, sk2, skProof2) } func Test_DevicePrivKey(t *testing.T) { acc1, err := secretstore.NewInMemSecretStore(nil) assert.NoError(t, err) assert.NotNil(t, acc1) sk1, skProof1, err := acc1.ExportAccountKeysForBackup() assert.NoError(t, err) assert.NotNil(t, sk1) assert.NotNil(t, skProof1) acc2, err := secretstore.NewInMemSecretStore(nil) assert.NoError(t, err) assert.NotNil(t, acc1) err = acc2.ImportAccountKeys(sk1, skProof1) assert.NoError(t, err) sk2, skProof2, err := acc2.ExportAccountKeysForBackup() assert.NoError(t, err) assert.NotNil(t, sk2) assert.NotNil(t, skProof2) accGroup1, memberDevice1, err := acc1.GetGroupForAccount() assert.NoError(t, err) assert.NotNil(t, accGroup1) memberDevice2, err := acc2.GetOwnMemberDeviceForGroup(accGroup1) assert.NoError(t, err) assert.NotNil(t, memberDevice2) assert.True(t, memberDevice1.Member().Equals(memberDevice2.Member())) assert.False(t, memberDevice1.Device().Equals(memberDevice2.Device())) } func Test_ContactGroupPrivKey(t *testing.T) { acc1, err := secretstore.NewInMemSecretStore(nil) assert.NoError(t, err) assert.NotNil(t, acc1) _, acc1MemberDevice, err := acc1.GetGroupForAccount() assert.NoError(t, err) assert.NotNil(t, acc1MemberDevice) acc2, err := secretstore.NewInMemSecretStore(nil) assert.NoError(t, err) assert.NotNil(t, acc2) _, acc2MemberDevice, err := acc2.GetGroupForAccount() assert.NoError(t, err) assert.NotNil(t, acc2MemberDevice) grp1, err := acc1.GetGroupForContact(acc2MemberDevice.Member()) assert.NoError(t, err) assert.NotNil(t, grp1) grp2, err := acc2.GetGroupForContact(acc1MemberDevice.Member()) assert.NoError(t, err) assert.NotNil(t, grp2) assert.Equal(t, grp1.PublicKey, grp2.PublicKey) assert.Equal(t, grp1.Secret, grp2.Secret) assert.Equal(t, grp1.GroupType, grp2.GroupType) } func Test_MemberDeviceForGroup_multimember(t *testing.T) { acc1, err := secretstore.NewInMemSecretStore(nil) assert.NoError(t, err) assert.NotNil(t, acc1) sk1, skProof1, err := acc1.ExportAccountKeysForBackup() assert.NoError(t, err) assert.NotNil(t, sk1) assert.NotNil(t, skProof1) acc2, err := secretstore.NewInMemSecretStore(nil) assert.NoError(t, err) assert.NotNil(t, acc2) err = acc2.ImportAccountKeys(sk1, skProof1) assert.NoError(t, err) sk2, skProof2, err := acc2.ExportAccountKeysForBackup() assert.NoError(t, err) assert.NotNil(t, sk2) assert.NotNil(t, skProof2) g, _, err := protocoltypes.NewGroupMultiMember() assert.NoError(t, err) omd1, err := acc1.GetOwnMemberDeviceForGroup(g) assert.NoError(t, err) omd2, err := acc2.GetOwnMemberDeviceForGroup(g) assert.NoError(t, err) omd1M := omd1.Member() omd2M := omd2.Member() omd1D := omd1.Device() omd2D := omd2.Device() assert.True(t, omd1M.Equals(omd2M)) assert.False(t, omd1D.Equals(omd2D)) } ================================================ FILE: pkg/secretstore/doc.go ================================================ // Package secretstore contains function related to device, groups and messages keys. package secretstore ================================================ FILE: pkg/secretstore/keys_utils.go ================================================ package secretstore import ( "crypto/ed25519" "crypto/sha256" "encoding/binary" "fmt" "io" "github.com/libp2p/go-libp2p/core/crypto" crypto_pb "github.com/libp2p/go-libp2p/core/crypto/pb" "golang.org/x/crypto/hkdf" "golang.org/x/crypto/sha3" "berty.tech/weshnet/v2/pkg/cryptoutil" "berty.tech/weshnet/v2/pkg/errcode" "berty.tech/weshnet/v2/pkg/protocoltypes" ) // getEd25519PrivateKeyFromLibP2PFormattedBytes transforms an exported LibP2P // private key into a crypto.PrivKey instance, ensuring it is an ed25519 key func getEd25519PrivateKeyFromLibP2PFormattedBytes(rawKeyBytes []byte) (crypto.PrivKey, error) { if len(rawKeyBytes) == 0 { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("missing key data")) } privateKey, err := crypto.UnmarshalPrivateKey(rawKeyBytes) if err != nil { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(err) } if privateKey.Type() != crypto_pb.KeyType_Ed25519 { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("invalid key format")) } return privateKey, nil } // getKeysForGroupOfContact returns derived private keys for contact group // using a private key via two accounts account keys (via an ECDH). func getKeysForGroupOfContact(contactPairPrivateKey crypto.PrivKey) (crypto.PrivKey, crypto.PrivKey, error) { // Salt length must be equal to hash length (64 bytes for sha256) hash := sha256.New contactPairPrivateKeyBytes, err := contactPairPrivateKey.Raw() if err != nil { return nil, nil, errcode.ErrCode_ErrSerialization.Wrap(err) } // Generate Pseudo Random Key using contactPairPrivateKeyBytes as IKM and salt prk := hkdf.Extract(hash, contactPairPrivateKeyBytes, nil) if len(prk) == 0 { return nil, nil, errcode.ErrCode_ErrInternal.Wrap(fmt.Errorf("unable to instantiate pseudo random key")) } // Expand using extracted prk and groupID as info (kind of namespace) kdf := hkdf.Expand(hash, prk, nil) // Generate next KDF and message keys groupSeed, err := io.ReadAll(io.LimitReader(kdf, 32)) if err != nil { return nil, nil, errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err) } groupSecretSeed, err := io.ReadAll(io.LimitReader(kdf, 32)) if err != nil { return nil, nil, errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err) } stdGroupPrivateKey := ed25519.NewKeyFromSeed(groupSeed) groupPrivateKey, _, err := crypto.KeyPairFromStdKey(&stdGroupPrivateKey) if err != nil { return nil, nil, errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err) } stdGroupSecretPrivateKey := ed25519.NewKeyFromSeed(groupSecretSeed) groupSecretPrivateKey, _, err := crypto.KeyPairFromStdKey(&stdGroupSecretPrivateKey) if err != nil { return nil, nil, errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err) } return groupPrivateKey, groupSecretPrivateKey, nil } // getGroupForContact returns a protocoltypes.Group instance for a contact, // using a private key via two accounts account keys (via an ECDH) func getGroupForContact(contactPairPrivateKey crypto.PrivKey) (*protocoltypes.Group, error) { groupPrivateKey, groupSecretPrivateKey, err := getKeysForGroupOfContact(contactPairPrivateKey) if err != nil { return nil, errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err) } pubBytes, err := groupPrivateKey.GetPublic().Raw() if err != nil { return nil, errcode.ErrCode_ErrSerialization.Wrap(err) } signingBytes, err := cryptoutil.SeedFromEd25519PrivateKey(groupSecretPrivateKey) if err != nil { return nil, errcode.ErrCode_ErrSerialization.Wrap(err) } return &protocoltypes.Group{ PublicKey: pubBytes, Secret: signingBytes, SecretSig: nil, GroupType: protocoltypes.GroupType_GroupTypeContact, }, nil } // getGroupOutOfStoreSecret retrieves the out of store group secret func getGroupOutOfStoreSecret(m *protocoltypes.Group) ([]byte, error) { if len(m.GetSecret()) == 0 { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("no secret known for group")) } arr := [cryptoutil.KeySize]byte{} kdf := hkdf.New(sha3.New256, m.GetSecret(), nil, []byte(namespaceOutOfStoreSecret)) if _, err := io.ReadFull(kdf, arr[:]); err != nil { return nil, errcode.ErrCode_ErrStreamRead.Wrap(err) } return arr[:], nil } // createOutOfStoreGroupReference creates a hash used to identify an out of // store (e.g. push notification) message origin func createOutOfStoreGroupReference(m *protocoltypes.Group, sender []byte, counter uint64) ([]byte, error) { secret, err := getGroupOutOfStoreSecret(m) if err != nil { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(err) } arr := [cryptoutil.KeySize]byte{} buf := make([]byte, 8) binary.BigEndian.PutUint64(buf, counter) kdf := hkdf.New(sha3.New256, secret, nil, append(sender, buf...)) if _, err := io.ReadFull(kdf, arr[:]); err != nil { return nil, errcode.ErrCode_ErrStreamRead.Wrap(err) } return arr[:], nil } ================================================ FILE: pkg/secretstore/member_device.go ================================================ package secretstore import ( "github.com/libp2p/go-libp2p/core/crypto" ) type ownMemberDevice struct { member crypto.PrivKey device crypto.PrivKey public *memberDevice } // newOwnMemberDevice instantiate a new ownMemberDevice allowing signing // and encrypting data as both a device or a member part of a group. // It also contains the public counterpart of the member and device keys. func newOwnMemberDevice(member, device crypto.PrivKey) *ownMemberDevice { return &ownMemberDevice{ member: member, device: device, public: newMemberDevice(member.GetPublic(), device.GetPublic()), } } func (d *ownMemberDevice) MemberSign(data []byte) ([]byte, error) { return d.member.Sign(data) } func (d *ownMemberDevice) DeviceSign(data []byte) ([]byte, error) { return d.device.Sign(data) } func (d *ownMemberDevice) Member() crypto.PubKey { return d.public.member } func (d *ownMemberDevice) Device() crypto.PubKey { return d.public.device } type memberDevice struct { member crypto.PubKey device crypto.PubKey } func NewMemberDevice(member, device crypto.PubKey) MemberDevice { return newMemberDevice(member, device) } func newMemberDevice(member, device crypto.PubKey) *memberDevice { return &memberDevice{ member: member, device: device, } } func (m *memberDevice) Member() crypto.PubKey { return m.member } func (m *memberDevice) Device() crypto.PubKey { return m.device } ================================================ FILE: pkg/secretstore/secret_store.go ================================================ package secretstore import ( "context" "fmt" "sync" "github.com/ipfs/go-cid" "github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-datastore/sync" "github.com/libp2p/go-libp2p/core/crypto" "go.uber.org/zap" "golang.org/x/crypto/nacl/secretbox" "google.golang.org/protobuf/proto" "berty.tech/weshnet/v2/internal/datastoreutil" "berty.tech/weshnet/v2/pkg/cryptoutil" "berty.tech/weshnet/v2/pkg/errcode" "berty.tech/weshnet/v2/pkg/ipfsutil" "berty.tech/weshnet/v2/pkg/protocoltypes" ) const ( namespaceDeviceKeystore = "device_keystore" namespaceOutOfStoreSecret = "push_secret_ref" // nolint:gosec ) type secretStore struct { logger *zap.Logger datastore datastore.Datastore deviceKeystore *deviceKeystore messageMutex sync.RWMutex preComputedKeysCount int precomputeOutOfStoreGroupRefsCount uint64 } func (o *NewSecretStoreOptions) applyDefaults(rootDatastore datastore.Datastore) { if o.Logger == nil { o.Logger = zap.NewNop() } if o.Keystore == nil { o.Keystore = ipfsutil.NewDatastoreKeystore(datastoreutil.NewNamespacedDatastore(rootDatastore, datastore.NewKey(namespaceDeviceKeystore))) } if o.PreComputedKeysCount <= 0 { o.PreComputedKeysCount = PrecomputeMessageKeyCount } if o.PrecomputeOutOfStoreGroupRefsCount <= 0 { o.PrecomputeOutOfStoreGroupRefsCount = PrecomputeOutOfStoreGroupRefsCount } } // NewSecretStore instantiates a new SecretStore func NewSecretStore(rootDatastore datastore.Datastore, opts *NewSecretStoreOptions) (SecretStore, error) { return newSecretStore(rootDatastore, opts) } // newSecretStore instantiates a new secretStore func newSecretStore(rootDatastore datastore.Datastore, opts *NewSecretStoreOptions) (*secretStore, error) { if rootDatastore == nil { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("a datastore is required")) } if opts == nil { opts = &NewSecretStoreOptions{} } opts.applyDefaults(rootDatastore) devKeystore := newDeviceKeystore(opts.Keystore, opts.Logger) store := &secretStore{ logger: opts.Logger, datastore: rootDatastore, deviceKeystore: devKeystore, preComputedKeysCount: opts.PreComputedKeysCount, precomputeOutOfStoreGroupRefsCount: uint64(opts.PrecomputeOutOfStoreGroupRefsCount), } return store, nil } // NewInMemSecretStore instantiates a SecretStore using a volatile backend. func NewInMemSecretStore(opts *NewSecretStoreOptions) (SecretStore, error) { return newInMemSecretStore(opts) } // newInMemSecretStore instantiates a secretStore using a volatile backend. func newInMemSecretStore(opts *NewSecretStoreOptions) (*secretStore, error) { return newSecretStore(dssync.MutexWrap(datastore.NewMapDatastore()), opts) } func (s *secretStore) Close() error { return nil } func (s *secretStore) PutGroup(ctx context.Context, g *protocoltypes.Group) error { pk, err := g.GetPubKey() if err != nil { return errcode.ErrCode_ErrInvalidInput.Wrap(err) } // TODO: check if partial group or full group and complete if necessary if ok, err := s.hasGroup(ctx, pk); err != nil { return errcode.ErrCode_ErrInvalidInput.Wrap(err) } else if ok { return nil } data, err := proto.Marshal(g) if err != nil { return errcode.ErrCode_ErrSerialization.Wrap(err) } if err := s.datastore.Put(ctx, dsKeyForGroup(g.GetPublicKey()), data); err != nil { return errcode.ErrCode_ErrKeystorePut.Wrap(err) } memberDevice, err := s.GetOwnMemberDeviceForGroup(g) if err != nil { return errcode.ErrCode_ErrInternal.Wrap(err) } // Force generation of chain key for own device _, err = s.GetShareableChainKey(ctx, g, memberDevice.Member()) if err != nil { return errcode.ErrCode_ErrInternal.Wrap(err) } return nil } func (s *secretStore) GetOwnMemberDeviceForGroup(g *protocoltypes.Group) (OwnMemberDevice, error) { return s.deviceKeystore.memberDeviceForGroup(g) } func (s *secretStore) OpenOutOfStoreMessage(ctx context.Context, payload []byte) (*protocoltypes.OutOfStoreMessage, *protocoltypes.Group, []byte, bool, error) { oosMessageEnv := &protocoltypes.OutOfStoreMessageEnvelope{} if err := proto.Unmarshal(payload, oosMessageEnv); err != nil { return nil, nil, nil, false, errcode.ErrCode_ErrDeserialization.Wrap(err) } groupPublicKey, err := s.OutOfStoreGetGroupPublicKeyByGroupReference(ctx, oosMessageEnv.GroupReference) if err != nil { return nil, nil, nil, false, errcode.ErrCode_ErrNotFound.Wrap(err) } oosMessage, err := s.decryptOutOfStoreMessageEnv(ctx, oosMessageEnv, groupPublicKey) if err != nil { return nil, nil, nil, false, errcode.ErrCode_ErrCryptoDecrypt.Wrap(err) } clear, newlyDecrypted, err := s.OutOfStoreMessageOpen(ctx, oosMessage, groupPublicKey) if err != nil { return nil, nil, nil, false, errcode.ErrCode_ErrCryptoDecrypt.Wrap(err) } group, err := s.FetchGroupByPublicKey(ctx, groupPublicKey) if err == nil { if err := s.UpdateOutOfStoreGroupReferences(ctx, oosMessage.DevicePk, oosMessage.Counter, group); err != nil { s.logger.Error("unable to update push group references", zap.Error(err)) } } return oosMessage, group, clear, !newlyDecrypted, nil } func (s *secretStore) decryptOutOfStoreMessageEnv(ctx context.Context, env *protocoltypes.OutOfStoreMessageEnvelope, groupPK crypto.PubKey) (*protocoltypes.OutOfStoreMessage, error) { nonce, err := cryptoutil.NonceSliceToArray(env.Nonce) if err != nil { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(err) } g, err := s.FetchGroupByPublicKey(ctx, groupPK) if err != nil { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("unable to find group, err: %w", err)) } secret := g.GetSharedSecret() data, ok := secretbox.Open(nil, env.Box, nonce, secret) if !ok { return nil, errcode.ErrCode_ErrCryptoDecrypt.Wrap(fmt.Errorf("unable to decrypt message")) } outOfStoreMessage := &protocoltypes.OutOfStoreMessage{} if err := proto.Unmarshal(data, outOfStoreMessage); err != nil { return nil, errcode.ErrCode_ErrDeserialization.Wrap(err) } return outOfStoreMessage, nil } func (s *secretStore) FetchGroupByPublicKey(ctx context.Context, publicKey crypto.PubKey) (*protocoltypes.Group, error) { keyBytes, err := publicKey.Raw() if err != nil { return nil, errcode.ErrCode_ErrSerialization.Wrap(err) } data, err := s.datastore.Get(ctx, dsKeyForGroup(keyBytes)) if err != nil { return nil, errcode.ErrCode_ErrMissingMapKey.Wrap(err) } g := &protocoltypes.Group{} if err := proto.Unmarshal(data, g); err != nil { return nil, errcode.ErrCode_ErrDeserialization.Wrap(err) } return g, nil } func (s *secretStore) GetAccountProofPublicKey() (crypto.PubKey, error) { privateKey, err := s.deviceKeystore.getAccountPrivateKey() if err != nil { return nil, errcode.ErrCode_ErrInternal.Wrap(err) } return privateKey.GetPublic(), nil } func (s *secretStore) ImportAccountKeys(accountPrivateKeyBytes []byte, accountProofPrivateKeyBytes []byte) error { return s.deviceKeystore.restoreAccountKeys(accountPrivateKeyBytes, accountProofPrivateKeyBytes) } func (s *secretStore) ExportAccountKeysForBackup() (accountPrivateKeyBytes []byte, accountProofPrivateKeyBytes []byte, err error) { accountPrivateKey, err := s.deviceKeystore.getAccountPrivateKey() if err != nil { return nil, nil, errcode.ErrCode_ErrInternal.Wrap(err) } accountProofPrivateKey, err := s.deviceKeystore.getAccountProofPrivateKey() if err != nil { return nil, nil, errcode.ErrCode_ErrInternal.Wrap(err) } accountPrivateKeyBytes, err = crypto.MarshalPrivateKey(accountPrivateKey) if err != nil { return nil, nil, errcode.ErrCode_ErrSerialization.Wrap(err) } accountProofPrivateKeyBytes, err = crypto.MarshalPrivateKey(accountProofPrivateKey) if err != nil { return nil, nil, errcode.ErrCode_ErrSerialization.Wrap(err) } return accountPrivateKeyBytes, accountProofPrivateKeyBytes, nil } func (s *secretStore) GetAccountPrivateKey() (crypto.PrivKey, error) { accountPrivateKey, err := s.deviceKeystore.getAccountPrivateKey() if err != nil { return nil, errcode.ErrCode_ErrInternal.Wrap(err) } return accountPrivateKey, nil } func (s *secretStore) GetGroupForAccount() (*protocoltypes.Group, OwnMemberDevice, error) { accountPrivateKey, err := s.deviceKeystore.getAccountPrivateKey() if err != nil { return nil, nil, errcode.ErrCode_ErrOrbitDBOpen.Wrap(err) } accountProofPrivateKey, err := s.deviceKeystore.getAccountProofPrivateKey() if err != nil { return nil, nil, errcode.ErrCode_ErrOrbitDBOpen.Wrap(err) } devicePrivateKey, err := s.deviceKeystore.devicePrivateKey() if err != nil { return nil, nil, errcode.ErrCode_ErrInternal.Wrap(err) } pubBytes, err := accountPrivateKey.GetPublic().Raw() if err != nil { return nil, nil, errcode.ErrCode_ErrSerialization.Wrap(err) } signingBytes, err := cryptoutil.SeedFromEd25519PrivateKey(accountProofPrivateKey) if err != nil { return nil, nil, errcode.ErrCode_ErrSerialization.Wrap(err) } return &protocoltypes.Group{ PublicKey: pubBytes, Secret: signingBytes, SecretSig: nil, GroupType: protocoltypes.GroupType_GroupTypeAccount, }, newOwnMemberDevice(accountPrivateKey, devicePrivateKey), nil } func (s *secretStore) GetGroupForContact(contactPublicKey crypto.PubKey) (*protocoltypes.Group, error) { contactPairPrivateKey, err := s.deviceKeystore.contactGroupPrivateKey(contactPublicKey) if err != nil { return nil, errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err) } return getGroupForContact(contactPairPrivateKey) } func (s *secretStore) OpenEnvelopeHeaders(data []byte, g *protocoltypes.Group) (*protocoltypes.MessageEnvelope, *protocoltypes.MessageHeaders, error) { env := &protocoltypes.MessageEnvelope{} err := proto.Unmarshal(data, env) if err != nil { return nil, nil, errcode.ErrCode_ErrDeserialization.Wrap(err) } nonce, err := cryptoutil.NonceSliceToArray(env.Nonce) if err != nil { return nil, nil, errcode.ErrCode_ErrSerialization.Wrap(fmt.Errorf("unable to convert slice to array: %w", err)) } headersBytes, ok := secretbox.Open(nil, env.MessageHeaders, nonce, g.GetSharedSecret()) if !ok { return nil, nil, errcode.ErrCode_ErrCryptoDecrypt.Wrap(fmt.Errorf("secretbox failed to open headers")) } headers := &protocoltypes.MessageHeaders{} if err := proto.Unmarshal(headersBytes, headers); err != nil { return nil, nil, errcode.ErrCode_ErrDeserialization.Wrap(err) } return env, headers, nil } func (s *secretStore) SealOutOfStoreMessageEnvelope(id cid.Cid, env *protocoltypes.MessageEnvelope, headers *protocoltypes.MessageHeaders, group *protocoltypes.Group) (*protocoltypes.OutOfStoreMessageEnvelope, error) { oosMessage := &protocoltypes.OutOfStoreMessage{ Cid: id.Bytes(), DevicePk: headers.DevicePk, Counter: headers.Counter, Sig: headers.Sig, EncryptedPayload: env.Message, Nonce: env.Nonce, } data, err := proto.Marshal(oosMessage) if err != nil { return nil, errcode.ErrCode_ErrSerialization.Wrap(err) } nonce, err := cryptoutil.GenerateNonce() if err != nil { return nil, errcode.ErrCode_ErrCryptoNonceGeneration.Wrap(err) } secret, err := cryptoutil.KeySliceToArray(group.Secret) if err != nil { return nil, errcode.ErrCode_ErrCryptoKeyConversion.Wrap(fmt.Errorf("unable to convert slice to array: %w", err)) } encryptedData := secretbox.Seal(nil, data, nonce, secret) pushGroupRef, err := createOutOfStoreGroupReference(group, headers.DevicePk, headers.Counter) if err != nil { return nil, errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err) } return &protocoltypes.OutOfStoreMessageEnvelope{ Nonce: nonce[:], Box: encryptedData, GroupReference: pushGroupRef, }, nil } // hasGroup checks whether a group is already known by the secretStore func (s *secretStore) hasGroup(ctx context.Context, key crypto.PubKey) (bool, error) { keyBytes, err := key.Raw() if err != nil { return false, errcode.ErrCode_ErrSerialization.Wrap(err) } return s.datastore.Has(ctx, dsKeyForGroup(keyBytes)) } ================================================ FILE: pkg/secretstore/secret_store_interfaces.go ================================================ package secretstore import ( "context" "github.com/ipfs/go-cid" keystore "github.com/ipfs/go-ipfs-keystore" "github.com/libp2p/go-libp2p/core/crypto" "go.uber.org/zap" "berty.tech/weshnet/v2/pkg/protocoltypes" ) const ( PrecomputeOutOfStoreGroupRefsCount = 100 PrecomputeMessageKeyCount = 100 ) type messageKey [32]byte type SecretStore interface { // // Account methods // // GetAccountProofPublicKey returns the user's account proof public key GetAccountProofPublicKey() (accountProofPublicKey crypto.PubKey, err error) // ImportAccountKeys restores backup of account keys into the SecretStore, it should fail if the store is already used by an account ImportAccountKeys(accountPrivateKey []byte, accountProofPrivateKey []byte) error // ExportAccountKeysForBackup returns the account's private key and proof private key of the user for a backup ExportAccountKeysForBackup() (accountPrivateKey []byte, accountProofPrivateKey []byte, err error) // GetAccountPrivateKey returns the account's private key, avoid using it, use GetGroupForAccount to get the account public key or sign data instead GetAccountPrivateKey() (accountPrivateKey crypto.PrivKey, err error) // // Groups methods // // GetGroupForAccount returns the Account's Group of the user GetGroupForAccount() (group *protocoltypes.Group, ownMemberDevice OwnMemberDevice, err error) // GetGroupForContact returns a contact group for communicating with the provided account GetGroupForContact(contactPublicKey crypto.PubKey) (group *protocoltypes.Group, err error) // PutGroup stores a group into the store PutGroup(ctx context.Context, group *protocoltypes.Group) error // FetchGroupByPublicKey gets an account from the store using the provided public key FetchGroupByPublicKey(ctx context.Context, publicKey crypto.PubKey) (group *protocoltypes.Group, err error) // // Envelopes methods // // OpenEnvelopeHeaders opens a message headers for a given group OpenEnvelopeHeaders(data []byte, group *protocoltypes.Group) (*protocoltypes.MessageEnvelope, *protocoltypes.MessageHeaders, error) // OpenEnvelopePayload opens a message payload with the given group headers OpenEnvelopePayload(ctx context.Context, msgEnvelope *protocoltypes.MessageEnvelope, msgHeaders *protocoltypes.MessageHeaders, groupPublicKey crypto.PubKey, ownPublicKey crypto.PubKey, msgCID cid.Cid) (*protocoltypes.EncryptedMessage, error) // SealEnvelope creates an encrypted payload to be sent to a group SealEnvelope(ctx context.Context, group *protocoltypes.Group, messagePayload []byte) (sealedEnvelope []byte, err error) // // Group member-device pairs methods // // GetOwnMemberDeviceForGroup gets a member and device key-pairs representing the current device in a given group GetOwnMemberDeviceForGroup(group *protocoltypes.Group) (OwnMemberDevice, error) // // Chain-keys methods // // RegisterChainKey records another device chain-key RegisterChainKey(ctx context.Context, group *protocoltypes.Group, senderDevicePublicKey crypto.PubKey, encryptedDeviceChainKey []byte) error // GetShareableChainKey returns a chain-key that can be decrypted by the provided member of a group GetShareableChainKey(ctx context.Context, group *protocoltypes.Group, targetMemberPublicKey crypto.PubKey) (encryptedDeviceChainKey []byte, err error) // IsChainKeyKnownForDevice checks whether a chain key of a device is already known IsChainKeyKnownForDevice(ctx context.Context, groupPublicKey crypto.PubKey, devicePublicKey crypto.PubKey) (isKnown bool) // // Out-of-store messages methods // // SealOutOfStoreMessageEnvelope encrypts a message to be sent outside a synchronized store SealOutOfStoreMessageEnvelope(id cid.Cid, env *protocoltypes.MessageEnvelope, headers *protocoltypes.MessageHeaders, group *protocoltypes.Group) (*protocoltypes.OutOfStoreMessageEnvelope, error) // OpenOutOfStoreMessage opens a message received outside a synchronized store OpenOutOfStoreMessage(ctx context.Context, payload []byte) (outOfStoreMessage *protocoltypes.OutOfStoreMessage, group *protocoltypes.Group, clearPayload []byte, alreadyDecrypted bool, err error) // UpdateOutOfStoreGroupReferences computes references of messages which might be received outside a synchronized store UpdateOutOfStoreGroupReferences(ctx context.Context, devicePublicKeyBytes []byte, first uint64, group *protocoltypes.Group) error // Close frees resources created by the secret store Close() error } // NewSecretStoreOptions contains the options that can be passed to NewSecretStore type NewSecretStoreOptions struct { // PreComputedKeysCount specifies the number of keys to precompute, // defaults to PrecomputeMessageKeyCount PreComputedKeysCount int // PrecomputeOutOfStoreGroupRefsCount specifies the number of out of store references // to precompute, defaults to PrecomputeOutOfStoreGroupRefsCount PrecomputeOutOfStoreGroupRefsCount int // Keystore specifies an implementation of a keystore to be used, can be // helpful if you want to rely on a hardware based keystore instead of a // software one Keystore keystore.Keystore // Logger specifies which logger to use, logging is disabled by default Logger *zap.Logger // DisableOutOfStoreSupport explicitly disables support of out-of-store // payloads DisableOutOfStoreSupport bool } // MemberDevice is the public keys of a device and its member type MemberDevice interface { // Member returns the member public key Member() crypto.PubKey // Device returns the device public key Device() crypto.PubKey } // OwnMemberDevice is a MemberDevice for the current device, able to sign data type OwnMemberDevice interface { MemberDevice // MemberSign signs the given data as a member of a group MemberSign(data []byte) ([]byte, error) // DeviceSign signs the given data as a device of a group DeviceSign(data []byte) ([]byte, error) } ================================================ FILE: pkg/secretstore/secret_store_messages.go ================================================ package secretstore import ( "context" "crypto/sha256" "encoding/binary" "fmt" "io" "github.com/ipfs/go-cid" "github.com/ipfs/go-datastore" "github.com/libp2p/go-libp2p/core/crypto" "go.uber.org/zap" "golang.org/x/crypto/hkdf" "golang.org/x/crypto/nacl/secretbox" "google.golang.org/protobuf/proto" "berty.tech/weshnet/v2/pkg/cryptoutil" "berty.tech/weshnet/v2/pkg/errcode" "berty.tech/weshnet/v2/pkg/logutil" "berty.tech/weshnet/v2/pkg/protocoltypes" ) // decryptionContext contains context about a decrypted message, its CID, the // associated message key and whether it had been previously opened. type decryptionContext struct { newlyDecrypted bool messageKey *messageKey cid cid.Cid } // computedMessageKey is a precomputed message key for a given counter used in the cache namespace. type computedMessageKey struct { counter uint64 messageKey *messageKey } // getDeviceChainKeyForGroupAndDevice returns the device chain key for the given group and device. func (s *secretStore) getDeviceChainKeyForGroupAndDevice(ctx context.Context, groupPublicKey crypto.PubKey, devicePublicKey crypto.PubKey) (*protocoltypes.DeviceChainKey, error) { if s == nil { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("calling method of a non instantiated message keystore")) } key, err := dsKeyForCurrentChainKey(groupPublicKey, devicePublicKey) if err != nil { return nil, errcode.ErrCode_ErrInternal.Wrap(err) } // Not mutex here dsBytes, err := s.datastore.Get(ctx, key) if err == datastore.ErrNotFound { return nil, errcode.ErrCode_ErrMissingInput.Wrap(err) } else if err != nil { return nil, errcode.ErrCode_ErrMessageKeyPersistenceGet.Wrap(err) } ds := &protocoltypes.DeviceChainKey{} if err := proto.Unmarshal(dsBytes, ds); err != nil { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(err) } return ds, nil } // IsChainKeyKnownForDevice returns true if the device chain key is known for the given group and device. func (s *secretStore) IsChainKeyKnownForDevice(ctx context.Context, groupPublicKey crypto.PubKey, devicePublicKey crypto.PubKey) (has bool) { if s == nil { return false } key, err := dsKeyForCurrentChainKey(groupPublicKey, devicePublicKey) if err != nil { return false } s.messageMutex.RLock() defer s.messageMutex.RUnlock() has, _ = s.datastore.Has(ctx, key) return } // delPrecomputedKey deletes the message key in the cache namespace for the given group, device and counter. func (s *secretStore) delPrecomputedKey(ctx context.Context, groupPublicKey crypto.PubKey, devicePublicKey crypto.PubKey, msgCounter uint64) error { if s == nil { return errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("calling method of a non instantiated message keystore")) } devicePublicKeyRaw, err := devicePublicKey.Raw() if err != nil { return errcode.ErrCode_ErrSerialization.Wrap(err) } groupPublicKeyRaw, err := groupPublicKey.Raw() if err != nil { return errcode.ErrCode_ErrSerialization.Wrap(err) } id := dsKeyForPrecomputedMessageKey(groupPublicKeyRaw, devicePublicKeyRaw, msgCounter) err = s.datastore.Delete(ctx, id) if err != nil { return errcode.ErrCode_ErrMessageKeyPersistencePut.Wrap(err) } return nil } // postDecryptActions is called after a message has been decrypted. // It saves the message key from the cache namespace to find it quickly on subsequent read operations. // It derives the chain key in the cache namespace. func (s *secretStore) postDecryptActions(ctx context.Context, decryptionCtx *decryptionContext, groupPublicKey crypto.PubKey, ownPublicKey crypto.PubKey, msgHeaders *protocoltypes.MessageHeaders) error { if s == nil { return errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("calling method of a non instantiated message keystore")) } // Message was newly decrypted, we can save the message key and derive // future keys if necessary. if decryptionCtx == nil || !decryptionCtx.newlyDecrypted { return nil } var ( deviceChainKey *protocoltypes.DeviceChainKey err error ) devicePublicKey, err := crypto.UnmarshalEd25519PublicKey(msgHeaders.DevicePk) if err != nil { return errcode.ErrCode_ErrDeserialization.Wrap(err) } if err = s.putKeyForCID(ctx, decryptionCtx.cid, decryptionCtx.messageKey); err != nil { return errcode.ErrCode_ErrInternal.Wrap(err) } if err = s.delPrecomputedKey(ctx, groupPublicKey, devicePublicKey, msgHeaders.Counter); err != nil { return errcode.ErrCode_ErrInternal.Wrap(err) } if deviceChainKey, err = s.preComputeNextKey(ctx, groupPublicKey, devicePublicKey); err != nil { return errcode.ErrCode_ErrInternal.Wrap(err) } // If the message was not emitted by the current Device we might need // to update the current chain key if ownPublicKey == nil || !ownPublicKey.Equals(devicePublicKey) { if err = s.updateCurrentKey(ctx, groupPublicKey, devicePublicKey, deviceChainKey); err != nil { return errcode.ErrCode_ErrInternal.Wrap(err) } } return nil } func (s *secretStore) GetShareableChainKey(ctx context.Context, group *protocoltypes.Group, targetMemberPublicKey crypto.PubKey) ([]byte, error) { deviceChainKey, err := s.getOwnDeviceChainKeyForGroup(ctx, group) if err != nil { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(err) } privateMemberDevice, err := s.deviceKeystore.memberDeviceForGroup(group) if err != nil { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(err) } encryptedDeviceChainKey, err := encryptDeviceChainKey(privateMemberDevice.device, targetMemberPublicKey, deviceChainKey, group) if err != nil { return nil, errcode.ErrCode_ErrCryptoEncrypt.Wrap(err) } return encryptedDeviceChainKey, nil } // getOwnDeviceChainKeyForGroup returns the device chain key for the current // device on a given group. // If the chain key has not been created yet, it will be generated and // registered. func (s *secretStore) getOwnDeviceChainKeyForGroup(ctx context.Context, group *protocoltypes.Group) (*protocoltypes.DeviceChainKey, error) { if s == nil { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("calling method of a non instantiated message keystore")) } if s.deviceKeystore == nil { return nil, errcode.ErrCode_ErrCryptoSignature.Wrap(fmt.Errorf("message keystore is opened in read-only mode")) } md, err := s.deviceKeystore.memberDeviceForGroup(group) if err != nil { return nil, errcode.ErrCode_ErrInternal.Wrap(err) } groupPublicKey, err := group.GetPubKey() if err != nil { return nil, errcode.ErrCode_ErrDeserialization.Wrap(err) } s.messageMutex.Lock() defer s.messageMutex.Unlock() ds, err := s.getDeviceChainKeyForGroupAndDevice(ctx, groupPublicKey, md.Device()) if errcode.Is(err, errcode.ErrCode_ErrMissingInput) { // If secret does not exist, create it deviceChainKey, err := newDeviceChainKey() if err != nil { return nil, errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err) } if err = s.registerChainKey(ctx, group, md.Device(), deviceChainKey, true); err != nil { return nil, errcode.ErrCode_ErrMessageKeyPersistencePut.Wrap(err) } return deviceChainKey, nil } if err != nil { return nil, errcode.ErrCode_ErrMessageKeyPersistenceGet.Wrap(err) } return ds, nil } // RegisterChainKey registers a chain key for the given group and device. // If the device chain key is not from the current device, the function will // precompute and store in the cache namespace the next message keys. // It is the exported version of registerChainKey. func (s *secretStore) RegisterChainKey(ctx context.Context, group *protocoltypes.Group, senderDevicePublicKey crypto.PubKey, encryptedDeviceChainKey []byte) error { if s == nil { return errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("calling method of a non instantiated message keystore")) } if s.deviceKeystore == nil { return errcode.ErrCode_ErrCryptoSignature.Wrap(fmt.Errorf("message keystore is opened in read-only mode")) } localMemberDevice, err := s.deviceKeystore.memberDeviceForGroup(group) if err != nil { return errcode.ErrCode_ErrGroupMemberUnknownGroupID.Wrap(err) } deviceChainKey, err := decryptDeviceChainKey(encryptedDeviceChainKey, group, localMemberDevice.member, senderDevicePublicKey) if err != nil { return errcode.ErrCode_ErrCryptoDecrypt.Wrap(err) } hasSecretBeenSentByCurrentDevice := localMemberDevice.Member().Equals(senderDevicePublicKey) return s.registerChainKey(ctx, group, senderDevicePublicKey, deviceChainKey, hasSecretBeenSentByCurrentDevice) } // registerChainKey registers a chain key for the given group and device. // If the chain key is not from the current device, the function will // precompute and store in the cache namespace the next message keys. func (s *secretStore) registerChainKey(ctx context.Context, group *protocoltypes.Group, devicePublicKey crypto.PubKey, deviceChainKey *protocoltypes.DeviceChainKey, isCurrentDeviceChainKey bool) error { if s == nil { return errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("calling method of a non instantiated message keystore")) } groupPublicKey, err := group.GetPubKey() if err != nil { return errcode.ErrCode_ErrDeserialization.Wrap(err) } if _, err := s.getDeviceChainKeyForGroupAndDevice(ctx, groupPublicKey, devicePublicKey); err == nil { // Device is already registered, ignore it s.logger.Debug("device already registered in group", logutil.PrivateBinary("devicePublicKey", logutil.CryptoKeyToBytes(devicePublicKey)), logutil.PrivateBinary("groupPublicKey", logutil.CryptoKeyToBytes(groupPublicKey)), ) return nil } s.logger.Debug("registering chain key", logutil.PrivateBinary("devicePublicKey", logutil.CryptoKeyToBytes(devicePublicKey)), logutil.PrivateBinary("groupPublicKey", logutil.CryptoKeyToBytes(groupPublicKey)), ) // If own Device store key as is, no need to precompute future keys if isCurrentDeviceChainKey { if err := s.putDeviceChainKey(ctx, groupPublicKey, devicePublicKey, deviceChainKey); err != nil { return errcode.ErrCode_ErrInternal.Wrap(err) } return nil } s.messageMutex.Lock() if deviceChainKey, err = s.preComputeKeys(ctx, devicePublicKey, groupPublicKey, deviceChainKey); err != nil { s.messageMutex.Unlock() return errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err) } if err := s.putDeviceChainKey(ctx, groupPublicKey, devicePublicKey, deviceChainKey); err != nil { s.messageMutex.Unlock() return errcode.ErrCode_ErrInternal.Wrap(err) } s.messageMutex.Unlock() devicePublicKeyBytes, err := devicePublicKey.Raw() if err == nil { if err := s.UpdateOutOfStoreGroupReferences(ctx, devicePublicKeyBytes, deviceChainKey.Counter, group); err != nil { s.logger.Error("updating out of store group references failed", zap.Error(err)) } } return nil } // preComputeKeys precomputes the next m.preComputedKeysCount keys for the given device and group and put them in the cache namespace. func (s *secretStore) preComputeKeys(ctx context.Context, devicePublicKey crypto.PubKey, groupPublicKey crypto.PubKey, deviceChainKey *protocoltypes.DeviceChainKey) (*protocoltypes.DeviceChainKey, error) { if s == nil { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("calling method of a non instantiated message keystore")) } chainKeyValue := deviceChainKey.ChainKey counter := deviceChainKey.Counter groupPublicKeyBytes, err := groupPublicKey.Raw() if err != nil { return nil, errcode.ErrCode_ErrSerialization.Wrap(err) } knownDeviceChainKey, err := s.getDeviceChainKeyForGroupAndDevice(ctx, groupPublicKey, devicePublicKey) if err != nil && !errcode.Is(err, errcode.ErrCode_ErrMissingInput) { return nil, errcode.ErrCode_ErrInternal.Wrap(err) } var preComputedKeys []computedMessageKey for i := 0; i < s.getPrecomputedKeyExpectedCount(); i++ { counter++ knownMK, err := s.getPrecomputedMessageKey(ctx, groupPublicKey, devicePublicKey, counter) if err != nil && !errcode.Is(err, errcode.ErrCode_ErrMissingInput) { return nil, errcode.ErrCode_ErrInternal.Wrap(err) } newChainKeyValue, mk, err := deriveNextKeys(chainKeyValue, nil, groupPublicKeyBytes) if err != nil { return nil, errcode.ErrCode_TODO.Wrap(err) } chainKeyValue = newChainKeyValue if knownMK != nil && knownDeviceChainKey != nil { if knownDeviceChainKey.Counter != counter-1 { continue } } preComputedKeys = append(preComputedKeys, computedMessageKey{counter, &mk}) } err = s.putPrecomputedKeys(ctx, groupPublicKey, devicePublicKey, preComputedKeys) if err != nil { return nil, errcode.ErrCode_TODO.Wrap(err) } return &protocoltypes.DeviceChainKey{ Counter: counter, ChainKey: chainKeyValue, }, nil } // getPrecomputedKeyExpectedCount returns the number of precomputed keys that // should be in the cache namespace of the keystore. func (s *secretStore) getPrecomputedKeyExpectedCount() int { if s == nil || s.preComputedKeysCount < 0 { return 0 } return s.preComputedKeysCount } // preComputeNextKey precomputes the next key for the given group and device and adds it to the cache namespace. func (s *secretStore) preComputeNextKey(ctx context.Context, groupPublicKey crypto.PubKey, devicePublicKey crypto.PubKey) (*protocoltypes.DeviceChainKey, error) { if s == nil { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("calling method of a non instantiated message keystore")) } if devicePublicKey == nil { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("devicePublicKey cannot be nil")) } groupPublicKeyBytes, err := groupPublicKey.Raw() if err != nil { return nil, errcode.ErrCode_ErrSerialization.Wrap(err) } ds, err := s.getDeviceChainKeyForGroupAndDevice(ctx, groupPublicKey, devicePublicKey) if err != nil { return nil, errcode.ErrCode_ErrInternal.Wrap(err) } newCounter := ds.Counter + 1 // TODO: Salt? newCK, mk, err := deriveNextKeys(ds.ChainKey, nil, groupPublicKeyBytes) if err != nil { return nil, errcode.ErrCode_TODO.Wrap(err) } err = s.putPrecomputedKeys(ctx, groupPublicKey, devicePublicKey, []computedMessageKey{{newCounter, &mk}}) if err != nil { return nil, errcode.ErrCode_TODO.Wrap(err) } return &protocoltypes.DeviceChainKey{ Counter: newCounter, ChainKey: newCK, }, nil } // getPrecomputedMessageKey returns the precomputed message key put in the cache // namespace for the given group and device at the given counter. func (s *secretStore) getPrecomputedMessageKey(ctx context.Context, groupPublicKey crypto.PubKey, devicePublicKey crypto.PubKey, counter uint64) (*messageKey, error) { if s == nil { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("calling method of a non instantiated message keystore")) } deviceRaw, err := devicePublicKey.Raw() if err != nil { return nil, errcode.ErrCode_ErrSerialization.Wrap(err) } groupRaw, err := groupPublicKey.Raw() if err != nil { return nil, errcode.ErrCode_ErrSerialization.Wrap(err) } id := dsKeyForPrecomputedMessageKey(groupRaw, deviceRaw, counter) key, err := s.datastore.Get(ctx, id) if err == datastore.ErrNotFound { return nil, errcode.ErrCode_ErrMissingInput.Wrap(fmt.Errorf("key for message does not exist in datastore")) } if err != nil { return nil, errcode.ErrCode_ErrMessageKeyPersistenceGet.Wrap(err) } keyArray, err := cryptoutil.KeySliceToArray(key) if err != nil { return nil, errcode.ErrCode_ErrSerialization.Wrap(err) } return (*messageKey)(keyArray), nil } // putPrecomputedKeys puts the given precomputed keys in the cache namespace. // It will try to use a batch if the store supports it. func (s *secretStore) putPrecomputedKeys(ctx context.Context, groupPublicKey crypto.PubKey, devicePublicKey crypto.PubKey, preComputedMessageKeys []computedMessageKey) error { if s == nil { return errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("calling method of a non instantiated message keystore")) } s.logger.Debug("putting precomputed keys", zap.Int("count", len(preComputedMessageKeys))) if len(preComputedMessageKeys) == 0 { return nil } deviceRaw, err := devicePublicKey.Raw() if err != nil { return errcode.ErrCode_ErrSerialization.Wrap(err) } groupRaw, err := groupPublicKey.Raw() if err != nil { return errcode.ErrCode_ErrSerialization.Wrap(err) } if batchedDatastore, ok := s.datastore.(datastore.BatchingFeature); ok { batch, err := batchedDatastore.Batch(ctx) if err == datastore.ErrBatchUnsupported { return s.putPrecomputedKeysNonBatched(ctx, groupRaw, deviceRaw, preComputedMessageKeys) } return s.putPrecomputedKeysBatched(ctx, batch, groupRaw, deviceRaw, preComputedMessageKeys) } return s.putPrecomputedKeysNonBatched(ctx, groupRaw, deviceRaw, preComputedMessageKeys) } func (s *secretStore) putPrecomputedKeysBatched(ctx context.Context, batch datastore.Batch, groupRaw []byte, deviceRaw []byte, preComputedMessageKeys []computedMessageKey) error { for _, preComputedKey := range preComputedMessageKeys { id := dsKeyForPrecomputedMessageKey(groupRaw, deviceRaw, preComputedKey.counter) if err := batch.Put(ctx, id, preComputedKey.messageKey[:]); err != nil { return errcode.ErrCode_ErrMessageKeyPersistencePut.Wrap(err) } } if err := batch.Commit(ctx); err != nil { return errcode.ErrCode_ErrMessageKeyPersistencePut.Wrap(err) } return nil } func (s *secretStore) putPrecomputedKeysNonBatched(ctx context.Context, groupRaw []byte, deviceRaw []byte, preComputedMessageKeys []computedMessageKey) error { for _, preComputedKey := range preComputedMessageKeys { id := dsKeyForPrecomputedMessageKey(groupRaw, deviceRaw, preComputedKey.counter) if err := s.datastore.Put(ctx, id, preComputedKey.messageKey[:]); err != nil { return errcode.ErrCode_ErrMessageKeyPersistencePut.Wrap(err) } } return nil } // putKeyForCID puts the given message key in the datastore for a specified CID. func (s *secretStore) putKeyForCID(ctx context.Context, messageCID cid.Cid, messageKey *messageKey) error { if s == nil { return errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("calling method of a non instantiated message keystore")) } if !messageCID.Defined() { return nil } err := s.datastore.Put(ctx, dsKeyForMessageKeyByCID(messageCID), messageKey[:]) if err != nil { return errcode.ErrCode_ErrMessageKeyPersistencePut.Wrap(err) } return nil } // OpenEnvelopePayload opens the payload of a message envelope and returns the // decrypted message in its EncryptedMessage form. // It also performs post decryption actions such as updating message key cache. func (s *secretStore) OpenEnvelopePayload(ctx context.Context, msgEnvelope *protocoltypes.MessageEnvelope, msgHeaders *protocoltypes.MessageHeaders, groupPublicKey crypto.PubKey, ownDevicePublicKey crypto.PubKey, msgCID cid.Cid) (*protocoltypes.EncryptedMessage, error) { s.messageMutex.Lock() defer s.messageMutex.Unlock() msgBytes, decryptionCtx, err := s.openPayload(ctx, msgCID, groupPublicKey, msgEnvelope.Message, msgHeaders) if err != nil { return nil, errcode.ErrCode_ErrCryptoDecryptPayload.Wrap(err) } if err := s.postDecryptActions(ctx, decryptionCtx, groupPublicKey, ownDevicePublicKey, msgHeaders); err != nil { return nil, errcode.ErrCode_TODO.Wrap(err) } var msg protocoltypes.EncryptedMessage err = proto.Unmarshal(msgBytes, &msg) if err != nil { return nil, errcode.ErrCode_ErrDeserialization.Wrap(err) } return &msg, nil } // openPayload opens the payload of a message envelope and returns the // decrypted message. // It retrieves the message key from the keystore or the cache to decrypt // the message. func (s *secretStore) openPayload(ctx context.Context, msgCID cid.Cid, groupPublicKey crypto.PubKey, payload []byte, msgHeaders *protocoltypes.MessageHeaders) ([]byte, *decryptionContext, error) { if s == nil { return nil, nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("calling method of a non instantiated message keystore")) } var ( err error decryptionCtx = &decryptionContext{ cid: msgCID, newlyDecrypted: true, } publicKey crypto.PubKey ) if decryptionCtx.messageKey, err = s.getKeyForCID(ctx, msgCID); err == nil { decryptionCtx.newlyDecrypted = false } else { publicKey, err = crypto.UnmarshalEd25519PublicKey(msgHeaders.DevicePk) if err != nil { return nil, nil, errcode.ErrCode_ErrDeserialization.Wrap(err) } decryptionCtx.messageKey, err = s.getPrecomputedMessageKey(ctx, groupPublicKey, publicKey, msgHeaders.Counter) if err != nil { return nil, nil, errcode.ErrCode_ErrCryptoDecrypt.Wrap(err) } } return s.openPayloadWithMessageKey(decryptionCtx, publicKey, payload, msgHeaders) } // openPayloadWithMessageKey opens the payload of a message envelope with the // given key and returns the decrypted message with the decryptionContext // struct. func (s *secretStore) openPayloadWithMessageKey(decryptionCtx *decryptionContext, devicePublicKey crypto.PubKey, payload []byte, headers *protocoltypes.MessageHeaders) ([]byte, *decryptionContext, error) { msg, ok := secretbox.Open(nil, payload, uint64AsNonce(headers.Counter), (*[32]byte)(decryptionCtx.messageKey)) if !ok { return nil, nil, errcode.ErrCode_ErrCryptoDecrypt.Wrap(fmt.Errorf("secret box failed to open message payload")) } if decryptionCtx.newlyDecrypted { if ok, err := devicePublicKey.Verify(msg, headers.Sig); !ok { return nil, nil, errcode.ErrCode_ErrCryptoSignatureVerification.Wrap(fmt.Errorf("unable to verify message signature")) } else if err != nil { return nil, nil, errcode.ErrCode_ErrCryptoSignatureVerification.Wrap(err) } } // Message was newly decrypted, we can save the message key and derive // future keys if necessary. return msg, decryptionCtx, nil } // getKeyForCID retrieves the message key for the given message CID. func (s *secretStore) getKeyForCID(ctx context.Context, msgCID cid.Cid) (*messageKey, error) { if s == nil { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("calling method of a non instantiated message keystore")) } if !msgCID.Defined() { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("undefined message CID")) } msgKey, err := s.datastore.Get(ctx, dsKeyForMessageKeyByCID(msgCID)) if err == datastore.ErrNotFound { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(err) } msgKeyArray, err := cryptoutil.KeySliceToArray(msgKey) if err != nil { return nil, errcode.ErrCode_ErrSerialization.Wrap(err) } return (*messageKey)(msgKeyArray), nil } // putDeviceChainKey stores the chain key for the given group and device. func (s *secretStore) putDeviceChainKey(ctx context.Context, groupPublicKey crypto.PubKey, devicePublicKey crypto.PubKey, deviceChainKey *protocoltypes.DeviceChainKey) error { if s == nil { return errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("calling method of a non instantiated message keystore")) } deviceChainKeyBytes, err := proto.Marshal(deviceChainKey) if err != nil { return errcode.ErrCode_ErrSerialization.Wrap(err) } datastoreKey, err := dsKeyForCurrentChainKey(groupPublicKey, devicePublicKey) if err != nil { return errcode.ErrCode_ErrSerialization.Wrap(err) } err = s.datastore.Put(ctx, datastoreKey, deviceChainKeyBytes) if err != nil { return errcode.ErrCode_ErrMessageKeyPersistencePut.Wrap(err) } return nil } // SealEnvelope encrypts the given payload and returns it as an envelope to be // published on the group's store. // It retrieves the device's chain key from the keystore to encrypt the payload // using symmetric encryption. The payload is signed using the device's long // term private key for the target group. It also updates the chain key and // stores the next message key in the cache. func (s *secretStore) SealEnvelope(ctx context.Context, group *protocoltypes.Group, messagePayload []byte) ([]byte, error) { if s == nil { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("calling method of a non instantiated message keystore")) } if s.deviceKeystore == nil { return nil, errcode.ErrCode_ErrCryptoSignature.Wrap(fmt.Errorf("message keystore is opened in read-only mode")) } if group == nil { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("group cannot be nil")) } localMemberDevice, err := s.deviceKeystore.memberDeviceForGroup(group) if err != nil { return nil, errcode.ErrCode_ErrGroupMemberUnknownGroupID.Wrap(err) } groupPublicKey, err := group.GetPubKey() if err != nil { return nil, errcode.ErrCode_ErrDeserialization.Wrap(err) } s.messageMutex.Lock() defer s.messageMutex.Unlock() deviceChainKey, err := s.getDeviceChainKeyForGroupAndDevice(ctx, groupPublicKey, localMemberDevice.Device()) if err != nil { return nil, errcode.ErrCode_ErrInternal.Wrap(fmt.Errorf("unable to get device chainkey: %w", err)) } env, err := sealEnvelope(messagePayload, deviceChainKey, localMemberDevice.device, group) if err != nil { return nil, errcode.ErrCode_ErrCryptoEncrypt.Wrap(fmt.Errorf("unable to seal envelope: %w", err)) } if err := s.deriveDeviceChainKey(ctx, group, localMemberDevice.Device()); err != nil { return nil, errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err) } return env, nil } // deriveDeviceChainKey derives the chain key value from the current one. // It also updates the device chain key in the keystore. func (s *secretStore) deriveDeviceChainKey(ctx context.Context, group *protocoltypes.Group, devicePublicKey crypto.PubKey) error { if s == nil { return errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("calling method of a non instantiated message keystore")) } if devicePublicKey == nil { return errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("device public key cannot be nil")) } groupPublicKey, err := group.GetPubKey() if err != nil { return errcode.ErrCode_ErrDeserialization.Wrap(err) } deviceChainKey, err := s.preComputeNextKey(ctx, groupPublicKey, devicePublicKey) if err != nil { return errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err) } if err = s.updateCurrentKey(ctx, groupPublicKey, devicePublicKey, deviceChainKey); err != nil { return errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err) } return nil } // updateCurrentKey updates the current device chain key in the keystore if the // given device secret has a higher counter. func (s *secretStore) updateCurrentKey(ctx context.Context, groupPublicKey crypto.PubKey, devicePublicKey crypto.PubKey, deviceChainKey *protocoltypes.DeviceChainKey) error { if s == nil { return errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("calling method of a non instantiated message keystore")) } currentDeviceChainKey, err := s.getDeviceChainKeyForGroupAndDevice(ctx, groupPublicKey, devicePublicKey) if err != nil { return errcode.ErrCode_ErrInternal.Wrap(err) } // FIXME: counter is set randomly and can overflow to 0 if deviceChainKey.Counter < currentDeviceChainKey.Counter { return nil } if err = s.putDeviceChainKey(ctx, groupPublicKey, devicePublicKey, deviceChainKey); err != nil { return errcode.ErrCode_ErrInternal.Wrap(err) } return nil } // OutOfStoreMessageOpen opens the given OutOfStoreMessage and returns the // decrypted payload. // The signature is verified against the given groupPublicKey. // It derives the next message key and stores it in the cache, but it doesn't // update the device's chain key. func (s *secretStore) OutOfStoreMessageOpen(ctx context.Context, envelope *protocoltypes.OutOfStoreMessage, groupPublicKey crypto.PubKey) ([]byte, bool, error) { if s == nil { return nil, false, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("calling method of a non instantiated message keystore")) } if envelope == nil { return nil, false, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("envelope cannot be nil")) } if groupPublicKey == nil { return nil, false, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("group public key cannot be nil")) } devicePublicKey, err := crypto.UnmarshalEd25519PublicKey(envelope.DevicePk) if err != nil { return nil, false, errcode.ErrCode_ErrDeserialization.Wrap(err) } c := cid.Undef if len(envelope.Cid) > 0 { _, c, err = cid.CidFromBytes(envelope.Cid) if err != nil { return nil, false, errcode.ErrCode_ErrDeserialization.Wrap(err) } } s.messageMutex.Lock() defer s.messageMutex.Unlock() decryptionCtx := &decryptionContext{newlyDecrypted: true} if decryptionCtx.messageKey, err = s.getKeyForCID(ctx, c); err == nil { decryptionCtx.newlyDecrypted = false } else { decryptionCtx.messageKey, err = s.getPrecomputedMessageKey(ctx, groupPublicKey, devicePublicKey, envelope.Counter) if err != nil { return nil, false, errcode.ErrCode_ErrCryptoDecrypt.Wrap(err) } } clear, decryptionCtx, err := s.openPayloadWithMessageKey(decryptionCtx, devicePublicKey, envelope.EncryptedPayload, &protocoltypes.MessageHeaders{ Counter: envelope.Counter, DevicePk: envelope.DevicePk, Sig: envelope.Sig, }) if err != nil { return nil, false, errcode.ErrCode_ErrCryptoDecrypt.Wrap(err) } if ok, err := devicePublicKey.Verify(clear, envelope.Sig); !ok { return nil, false, errcode.ErrCode_ErrCryptoSignatureVerification.Wrap(fmt.Errorf("unable to verify message signature")) } else if err != nil { return nil, false, errcode.ErrCode_ErrCryptoSignatureVerification.Wrap(err) } if _, err = s.preComputeNextKey(ctx, groupPublicKey, devicePublicKey); err != nil { return nil, false, errcode.ErrCode_ErrInternal.Wrap(err) } return clear, decryptionCtx.newlyDecrypted, nil } // OutOfStoreGetGroupPublicKeyByGroupReference returns the group public key // associated with the given out of store group reference (e.g. push // notification payload). func (s *secretStore) OutOfStoreGetGroupPublicKeyByGroupReference(ctx context.Context, ref []byte) (crypto.PubKey, error) { s.messageMutex.RLock() pk, err := s.datastore.Get(ctx, dsKeyForOutOfStoreMessageGroupHint(ref)) s.messageMutex.RUnlock() if err != nil { return nil, errcode.ErrCode_ErrNotFound.Wrap(err) } groupPublicKey, err := crypto.UnmarshalEd25519PublicKey(pk) if err != nil { return nil, errcode.ErrCode_ErrDeserialization.Wrap(err) } return groupPublicKey, nil } // UpdateOutOfStoreGroupReferences updates the out of store (e.g. push // notification payload) group references for the given devicePublicKey and // groupPublicKey in the keystore. It creates the references for the // given range [first + precomputeOutOfStoreGroupRefsCount] and // [first - precomputeOutOfStoreGroupRefsCount] and deletes out of range // references. func (s *secretStore) UpdateOutOfStoreGroupReferences(ctx context.Context, devicePublicKey []byte, first uint64, group *protocoltypes.Group) error { s.messageMutex.Lock() defer s.messageMutex.Unlock() refsExisting := []uint64(nil) refsToCreate := []uint64(nil) currentFirst, currentLast, err := s.firstLastCachedGroupRefsForMember(ctx, devicePublicKey, group) if err == nil { for i := currentFirst; i != currentLast; i++ { refsExisting = append(refsExisting, i) } } // keep previous refs last := first + s.precomputeOutOfStoreGroupRefsCount first -= s.precomputeOutOfStoreGroupRefsCount for i := first; i != last; i++ { found := false // Ignore refs that should be kept for j := 0; j < len(refsExisting); j++ { if refsExisting[j] == i { refsExisting[j] = refsExisting[len(refsExisting)-1] refsExisting = refsExisting[:len(refsExisting)-1] found = true break } } if !found { refsToCreate = append(refsToCreate, i) } } // Remove useless old refs for i := 0; i < len(refsExisting); i++ { ref, err := createOutOfStoreGroupReference(group, devicePublicKey, refsExisting[i]) if err != nil { s.logger.Error("creating existing out of store group reference failed", logutil.PrivateBinary("ref", ref), zap.Error(err)) continue } if err := s.datastore.Delete(ctx, dsKeyForOutOfStoreMessageGroupHint(ref)); err != nil { s.logger.Error("deleting existing out of store group reference failed", logutil.PrivateBinary("ref", ref), zap.Error(err)) continue } } // Add new refs for i := 0; i < len(refsToCreate); i++ { ref, err := createOutOfStoreGroupReference(group, devicePublicKey, refsToCreate[i]) if err != nil { s.logger.Error("creating new out of store group reference failed", logutil.PrivateBinary("ref", ref), zap.Error(err)) continue } if err := s.datastore.Put(ctx, dsKeyForOutOfStoreMessageGroupHint(ref), group.GetPublicKey()); err != nil { s.logger.Error("putting new out of store group reference failed", logutil.PrivateBinary("ref", ref), zap.Error(err)) continue } } // Update first/last if err := s.putFirstLastCachedGroupRefsForMember(ctx, first, last, devicePublicKey, group); err != nil { s.logger.Error("putting first/last out of store group reference failed", zap.Error(err)) } return nil } // firstLastCachedGroupRefsForMember returns the first and last cached group // references counter for the given devicePublicKey and group. func (s *secretStore) firstLastCachedGroupRefsForMember(ctx context.Context, devicePublicKeyBytes []byte, group *protocoltypes.Group) (uint64, uint64, error) { key := dsKeyForOutOfStoreFirstLastCounters(group.GetPublicKey(), devicePublicKeyBytes) // No mutex here bytes, err := s.datastore.Get(ctx, key) if err != nil { return 0, 0, errcode.ErrCode_ErrDBRead.Wrap(err) } ret := protocoltypes.FirstLastCounters{} if err := proto.Unmarshal(bytes, &ret); err != nil { return 0, 0, errcode.ErrCode_ErrDeserialization.Wrap(err) } return ret.First, ret.Last, nil } // putFirstLastCachedGroupRefsForMember puts the first and last cached group // references counter for the given devicePK and groupPK. func (s *secretStore) putFirstLastCachedGroupRefsForMember(ctx context.Context, first uint64, last uint64, devicePublicKey []byte, group *protocoltypes.Group) error { key := dsKeyForOutOfStoreFirstLastCounters(group.GetPublicKey(), devicePublicKey) fistLast := protocoltypes.FirstLastCounters{ First: first, Last: last, } bytes, err := proto.Marshal(&fistLast) if err != nil { return errcode.ErrCode_ErrSerialization.Wrap(err) } // Not mutex here return s.datastore.Put(ctx, key, bytes) } func sealPayload(payload []byte, ds *protocoltypes.DeviceChainKey, devicePrivateKey crypto.PrivKey, g *protocoltypes.Group) ([]byte, []byte, error) { var ( msgKey [32]byte err error ) sig, err := devicePrivateKey.Sign(payload) if err != nil { return nil, nil, errcode.ErrCode_ErrCryptoSignature.Wrap(err) } if _, msgKey, err = deriveNextKeys(ds.ChainKey, nil, g.GetPublicKey()); err != nil { return nil, nil, errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err) } return secretbox.Seal(nil, payload, uint64AsNonce(ds.Counter+1), &msgKey), sig, nil } func sealEnvelope(messagePayload []byte, deviceChainKey *protocoltypes.DeviceChainKey, devicePrivateKey crypto.PrivKey, g *protocoltypes.Group) ([]byte, error) { encryptedPayload, sig, err := sealPayload(messagePayload, deviceChainKey, devicePrivateKey, g) if err != nil { return nil, errcode.ErrCode_ErrCryptoEncrypt.Wrap(err) } devicePublicKeyRaw, err := devicePrivateKey.GetPublic().Raw() if err != nil { return nil, errcode.ErrCode_ErrSerialization.Wrap(err) } h := &protocoltypes.MessageHeaders{ Counter: deviceChainKey.Counter + 1, DevicePk: devicePublicKeyRaw, Sig: sig, } headers, err := proto.Marshal(h) if err != nil { return nil, errcode.ErrCode_ErrSerialization.Wrap(err) } nonce, err := cryptoutil.GenerateNonce() if err != nil { return nil, errcode.ErrCode_ErrCryptoNonceGeneration.Wrap(err) } encryptedHeaders := secretbox.Seal(nil, headers, nonce, g.GetSharedSecret()) env, err := proto.Marshal(&protocoltypes.MessageEnvelope{ MessageHeaders: encryptedHeaders, Message: encryptedPayload, Nonce: nonce[:], }) if err != nil { return nil, errcode.ErrCode_ErrSerialization.Wrap(err) } return env, nil } // nolint:unparam func deriveNextKeys(chainKeyValue []byte, salt []byte, groupID []byte) ([]byte, messageKey, error) { var ( nextMsg [32]byte err error ) // Salt length must be equal to hash length (64 bytes for sha256) hash := sha256.New // Generate Pseudo Random Key using chainKeyValue as IKM and salt prk := hkdf.Extract(hash, chainKeyValue, salt) if len(prk) == 0 { return nil, nextMsg, errcode.ErrCode_ErrInternal.Wrap(fmt.Errorf("unable to instantiate pseudo random key")) } // Expand using extracted prk and groupID as info (kind of namespace) kdf := hkdf.Expand(hash, prk, groupID) // Generate next KDF and message keys nextCK, err := io.ReadAll(io.LimitReader(kdf, 32)) if err != nil { return nil, nextMsg, errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err) } nextMsgSlice, err := io.ReadAll(io.LimitReader(kdf, 32)) if err != nil { return nil, nextMsg, errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err) } copy(nextMsg[:], nextMsgSlice) return nextCK, nextMsg, nil } func uint64AsNonce(val uint64) *[24]byte { var nonce [24]byte binary.BigEndian.PutUint64(nonce[:], val) return &nonce } ================================================ FILE: pkg/secretstore/secret_store_messages_test.go ================================================ package secretstore_test import ( "context" "fmt" "os" "path" "testing" "time" cid "github.com/ipfs/go-cid" "github.com/libp2p/go-libp2p/core/crypto" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" "berty.tech/weshnet/v2" "berty.tech/weshnet/v2/pkg/protocoltypes" "berty.tech/weshnet/v2/pkg/secretstore" "berty.tech/weshnet/v2/pkg/testutil" ) func addDummyMemberInMetadataStore(ctx context.Context, t testing.TB, ms *weshnet.MetadataStore, g *protocoltypes.Group, memberPK crypto.PubKey, join bool) crypto.PubKey { t.Helper() secretStore, err := secretstore.NewInMemSecretStore(nil) assert.NoError(t, err) assert.NotNil(t, secretStore) md, err := secretStore.GetOwnMemberDeviceForGroup(g) assert.NoError(t, err) if join { _, err = weshnet.MetadataStoreAddDeviceToGroup(ctx, ms, g, md) assert.NoError(t, err) } deviceChainKeyToSend, err := secretStore.GetShareableChainKey(ctx, g, memberPK) assert.NoError(t, err) _, err = weshnet.MetadataStoreSendSecret(ctx, ms, g, md, memberPK, deviceChainKeyToSend) assert.NoError(t, err) return md.Device() } func Test_EncryptMessageEnvelope(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() g, _, err := weshnet.NewGroupMultiMember() assert.NoError(t, err) secretStore1, err := secretstore.NewInMemSecretStore(nil) assert.NoError(t, err) assert.NotNil(t, secretStore1) t.Cleanup(func() { _ = secretStore1.Close() }) omd1, err := secretStore1.GetOwnMemberDeviceForGroup(g) assert.NoError(t, err) gc1 := weshnet.NewContextGroup(g, nil, nil, secretStore1, omd1, nil) deviceChainKey1For1, err := secretStore1.GetShareableChainKey(ctx, g, gc1.MemberPubKey()) assert.NoError(t, err) err = secretStore1.RegisterChainKey(ctx, g, gc1.DevicePubKey(), deviceChainKey1For1) assert.NoError(t, err) secretStore2, err := secretstore.NewInMemSecretStore(nil) assert.NoError(t, err) assert.NotNil(t, secretStore2) t.Cleanup(func() { _ = secretStore2.Close() }) omd2, err := secretStore2.GetOwnMemberDeviceForGroup(g) assert.NoError(t, err) payloadRef1, err := proto.Marshal(&protocoltypes.EncryptedMessage{Plaintext: []byte("Test payload 1")}) assert.NoError(t, err) deviceChainKey1For2, err := secretStore1.GetShareableChainKey(ctx, g, omd2.Member()) assert.NoError(t, err) deviceChainKey2For2, err := secretStore2.GetShareableChainKey(ctx, g, omd2.Member()) assert.NoError(t, err) err = secretStore2.RegisterChainKey(ctx, g, omd2.Device(), deviceChainKey2For2) assert.NoError(t, err) err = secretStore2.RegisterChainKey(ctx, g, omd1.Device(), deviceChainKey1For2) assert.NoError(t, err) env1, err := secretStore1.SealEnvelope(ctx, g, payloadRef1) assert.NoError(t, err) headers, payloadClr1, err := openEnvelope(ctx, t, secretStore2, g, omd2.Device(), env1, cid.Undef) assert.NoError(t, err) devRaw, err := omd1.Device().Raw() assert.Equal(t, headers.DevicePk, devRaw) payloadClrlBytes, err := proto.Marshal(payloadClr1) assert.NoError(t, err) assert.Equal(t, payloadRef1, payloadClrlBytes) } func testMessageKeyHolderCatchUp(t *testing.T, expectedNewDevices int, isSlow bool) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() if isSlow { testutil.FilterSpeed(t, testutil.Slow) } dir := path.Join(os.TempDir(), fmt.Sprintf("%d", os.Getpid()), "MessageKeyHolderCatchUp") defer os.RemoveAll(dir) peers, _, cleanup := weshnet.CreatePeersWithGroupTest(ctx, t, dir, 1, 1) defer cleanup() peer := peers[0] peer.GC.ActivateGroupContext(nil) secretStore1 := peer.SecretStore ms1 := peer.GC.MetadataStore() groupPublicKey, err := peer.GC.Group().GetPubKey() assert.NoError(t, err) devicesPK := make([]crypto.PubKey, expectedNewDevices) for i := 0; i < expectedNewDevices; i++ { devicesPK[i] = addDummyMemberInMetadataStore(ctx, t, ms1, peer.GC.Group(), peer.GC.MemberPubKey(), true) } for i, devicePublicKey := range devicesPK { select { case <-time.After(time.Second): require.FailNow(t, "timeout while waiting for device secret") case <-peer.GC.WaitForDeviceAdded(ctx, devicePublicKey): } if !assert.True(t, secretStore1.IsChainKeyKnownForDevice(ctx, groupPublicKey, devicePublicKey)) { t.Fatalf("failed at iteration %d", i) } } } func TestMessageKeyHolderCatchUp(t *testing.T) { for _, testCase := range []struct { expectedNewDevices int slow bool }{ { expectedNewDevices: 2, slow: false, }, { expectedNewDevices: 10, slow: true, }, } { testMessageKeyHolderCatchUp(t, testCase.expectedNewDevices, testCase.slow) } } func testMessageKeyHolderSubscription(t *testing.T, expectedNewDevices int, isSlow bool) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() if isSlow { testutil.FilterSpeed(t, testutil.Slow) } dir := path.Join(os.TempDir(), fmt.Sprintf("%d", os.Getpid()), "MessageKeyHolderSubscription") defer os.RemoveAll(dir) peers, groupPrivateKey, cleanup := weshnet.CreatePeersWithGroupTest(ctx, t, dir, 1, 1) defer cleanup() peer := peers[0] peer.GC.ActivateGroupContext(nil) secretStore1 := peer.SecretStore ms1 := peer.GC.MetadataStore() devicesPK := make([]crypto.PubKey, expectedNewDevices) for i := 0; i < expectedNewDevices; i++ { devicesPK[i] = addDummyMemberInMetadataStore(ctx, t, ms1, peer.GC.Group(), peer.GC.MemberPubKey(), true) } for i, devicePublicKey := range devicesPK { select { case <-time.After(time.Second): require.FailNow(t, "timeout while waiting for device secret") case <-peer.GC.WaitForDeviceAdded(ctx, devicePublicKey): } if !assert.True(t, secretStore1.IsChainKeyKnownForDevice(ctx, groupPrivateKey.GetPublic(), devicePublicKey)) { t.Fatalf("failed at iteration %d", i) } } } func TestMessageKeyHolderSubscription(t *testing.T) { for _, testCase := range []struct { expectedNewDevices int slow bool }{ { expectedNewDevices: 2, slow: false, }, { expectedNewDevices: 10, slow: true, }, } { testMessageKeyHolderSubscription(t, testCase.expectedNewDevices, testCase.slow) } } // openEnvelope opens a MessageEnvelope and returns the decrypted message. // It performs all the necessary steps to decrypt the message. func 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) { t.Helper() assert.NotNil(t, secretStore) assert.NotNil(t, g) env, headers, err := secretStore.OpenEnvelopeHeaders(data, g) assert.NoError(t, err) gPK, err := g.GetPubKey() assert.NoError(t, err) msg, err := secretStore.OpenEnvelopePayload(ctx, env, headers, gPK, ownPK, id) assert.NoError(t, err) return headers, msg, nil } ================================================ FILE: pkg/secretstore/secret_store_test.go ================================================ package secretstore import ( "context" crand "crypto/rand" "encoding/hex" "fmt" "testing" "github.com/ipfs/go-cid" "github.com/ipfs/go-datastore" "github.com/libp2p/go-libp2p/core/crypto" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" "berty.tech/weshnet/v2/pkg/protocoltypes" ) func Test_PushGroupReferences(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() g, _, err := protocoltypes.NewGroupMultiMember() require.NoError(t, err) otherSecretStore, err := newInMemSecretStore(nil) require.NoError(t, err) t.Cleanup(func() { _ = otherSecretStore.Close() }) ownSecretStore, err := newInMemSecretStore(nil) require.NoError(t, err) t.Cleanup(func() { _ = ownSecretStore.Close() }) otherMemberDevice, err := otherSecretStore.GetOwnMemberDeviceForGroup(g) require.NoError(t, err) otherDevicePK, err := otherMemberDevice.Device().Raw() require.NoError(t, err) deviceChainKey, err := newDeviceChainKey() require.NoError(t, err) // test with the deviceChainKey counter updateAndTestPushGroupReferences(ctx, ownSecretStore, otherDevicePK, deviceChainKey.Counter, g, t) // do the same test with a new device chain key counter, // so we can test if old references are deleted updateAndTestPushGroupReferences(ctx, ownSecretStore, otherDevicePK, deviceChainKey.Counter+10, g, t) } func updateAndTestPushGroupReferences(ctx context.Context, secretStore *secretStore, devicePK []byte, counter uint64, g *protocoltypes.Group, t *testing.T) { // update the push group references err := secretStore.UpdateOutOfStoreGroupReferences(ctx, devicePK, counter, g) require.NoError(t, err) // test that the push group references are updated // refs start counter - 100 to counter + 100 start := counter - PrecomputeOutOfStoreGroupRefsCount end := counter + PrecomputeOutOfStoreGroupRefsCount for i := start; i < end; i++ { // compute the push group reference pushGroupRef, err := createOutOfStoreGroupReference(g, devicePK, i) require.NoError(t, err) _, err = secretStore.OutOfStoreGetGroupPublicKeyByGroupReference(ctx, pushGroupRef) require.NoError(t, err, fmt.Sprintf("started at %d, failed as %d", start, i)) } // test boundary conditions // before the start counter { before := counter - PrecomputeOutOfStoreGroupRefsCount - 1 pushGroupRef, err := createOutOfStoreGroupReference(g, devicePK, before) require.NoError(t, err) _, err = secretStore.OutOfStoreGetGroupPublicKeyByGroupReference(ctx, pushGroupRef) require.Error(t, err) } // after the end counter { end := counter + PrecomputeOutOfStoreGroupRefsCount + 1 pushGroupRef, err := createOutOfStoreGroupReference(g, devicePK, end) require.NoError(t, err) _, err = secretStore.OutOfStoreGetGroupPublicKeyByGroupReference(ctx, pushGroupRef) require.Error(t, err) } } func Test_SealOutOfStoreMessageEnvelope_OpenOutOfStoreMessage(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() g, _, err := protocoltypes.NewGroupMultiMember() require.NoError(t, err) accUnrelated, err := newInMemSecretStore(nil) require.NoError(t, err) require.NotNil(t, accUnrelated) t.Cleanup(func() { _ = accUnrelated.Close() }) err = accUnrelated.PutGroup(ctx, g) require.NoError(t, err) acc1, err := newInMemSecretStore(nil) require.NoError(t, err) t.Cleanup(func() { _ = acc1.Close() }) acc2, err := newInMemSecretStore(nil) require.NoError(t, err) t.Cleanup(func() { _ = acc1.Close() }) memberDevice1ForGroup, err := acc1.GetOwnMemberDeviceForGroup(g) require.NoError(t, err) memberDevice2ForGroup, err := acc2.GetOwnMemberDeviceForGroup(g) require.NoError(t, err) deviceChainKey1For2, err := acc1.GetShareableChainKey(ctx, g, memberDevice2ForGroup.Member()) require.NoError(t, err) testPayload := []byte("test payload") err = acc2.RegisterChainKey(ctx, g, memberDevice1ForGroup.Device(), deviceChainKey1For2) require.NoError(t, err) envEncrypted, err := acc1.SealEnvelope(ctx, g, testPayload) require.NoError(t, err) env, headers, err := ((*secretStore)(nil)).OpenEnvelopeHeaders(envEncrypted, g) require.NoError(t, err) outOfStoreEnv, err := (*secretStore)(nil).SealOutOfStoreMessageEnvelope(cid.Undef, env, headers, g) require.NoError(t, err) groupPublicKey, err := acc2.OutOfStoreGetGroupPublicKeyByGroupReference(ctx, outOfStoreEnv.GroupReference) require.NoError(t, err) outOfStoreMessage, err := accUnrelated.decryptOutOfStoreMessageEnv(ctx, outOfStoreEnv, groupPublicKey) require.NoError(t, err) payload, newlyDecrypted, err := acc2.OutOfStoreMessageOpen(ctx, outOfStoreMessage, groupPublicKey) require.NoError(t, err) require.True(t, newlyDecrypted) require.Equal(t, testPayload, payload) } func Test_OutOfStoreDeserialize(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() secretStore1, err := NewSecretStore(datastore.NewMapDatastore(), nil) require.NoError(t, err) t.Cleanup(func() { _ = secretStore1.Close() }) secretStore2, err := NewSecretStore(datastore.NewMapDatastore(), nil) require.NoError(t, err) t.Cleanup(func() { _ = secretStore2.Close() }) group, _, err := protocoltypes.NewGroupMultiMember() require.NoError(t, err) require.NoError(t, secretStore1.PutGroup(ctx, group)) require.NoError(t, secretStore2.PutGroup(ctx, group)) memberDevice1, err := secretStore1.GetOwnMemberDeviceForGroup(group) require.NoError(t, err) memberDevice2, err := secretStore2.GetOwnMemberDeviceForGroup(group) require.NoError(t, err) chainKey1For2, err := secretStore1.GetShareableChainKey(ctx, group, memberDevice2.Member()) require.NoError(t, err) chainKey2For1, err := secretStore2.GetShareableChainKey(ctx, group, memberDevice1.Member()) require.NoError(t, err) require.NoError(t, secretStore1.RegisterChainKey(ctx, group, memberDevice2.Device(), chainKey2For1)) require.NoError(t, secretStore2.RegisterChainKey(ctx, group, memberDevice1.Device(), chainKey1For2)) testPayload := []byte("test payload") env, err := secretStore1.SealEnvelope(ctx, group, testPayload) require.NoError(t, err) envHeaders, msgHeaders, err := secretStore1.OpenEnvelopeHeaders(env, group) require.NoError(t, err) dummyCID, err := cid.Parse("QmNR2n4zywCV61MeMLB6JwPueAPqheqpfiA4fLPMxouEmQ") require.NoError(t, err) device1Raw, err := memberDevice1.Device().Raw() require.NoError(t, err) outOfStoreMessageEnvelope, err := secretStore1.SealOutOfStoreMessageEnvelope(dummyCID, envHeaders, msgHeaders, group) require.NoError(t, err) outOfStoreMessageEnvelopeBytes, err := proto.Marshal(outOfStoreMessageEnvelope) require.NoError(t, err) // Attempting to decrypt the message without a relay { openedOutOfStoreMessage, groupFound, clearPayload, alreadyDecrypted, err := secretStore2.OpenOutOfStoreMessage(ctx, outOfStoreMessageEnvelopeBytes) require.NoError(t, err) require.Equal(t, testPayload, clearPayload) require.Equal(t, device1Raw, openedOutOfStoreMessage.DevicePk) require.Equal(t, dummyCID.Bytes(), openedOutOfStoreMessage.Cid) require.Equal(t, group.PublicKey, groupFound.PublicKey) require.False(t, alreadyDecrypted) } } func Test_EncryptMessageEnvelopeAndDerive(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() g, _, err := protocoltypes.NewGroupMultiMember() assert.NoError(t, err) mkh1, err := newInMemSecretStore(nil) assert.NoError(t, err) t.Cleanup(func() { _ = mkh1.Close() }) mkh2, err := newInMemSecretStore(nil) assert.NoError(t, err) t.Cleanup(func() { _ = mkh1.Close() }) omd1, err := mkh1.GetOwnMemberDeviceForGroup(g) assert.NoError(t, err) omd2, err := mkh2.GetOwnMemberDeviceForGroup(g) assert.NoError(t, err) gPK, err := g.GetPubKey() assert.NoError(t, err) gc1DevicePubKey := omd1.Device() ds1For1Encrypted, err := mkh1.GetShareableChainKey(ctx, g, omd1.Member()) assert.NoError(t, err) ds1For2Encrypted, err := mkh1.GetShareableChainKey(ctx, g, omd2.Member()) assert.NoError(t, err) ds2For2Encrypted, err := mkh2.GetShareableChainKey(ctx, g, omd2.Member()) assert.NoError(t, err) err = mkh1.RegisterChainKey(ctx, g, omd1.Device(), ds1For1Encrypted) assert.NoError(t, err) err = mkh2.RegisterChainKey(ctx, g, omd2.Device(), ds2For2Encrypted) assert.NoError(t, err) err = mkh2.RegisterChainKey(ctx, g, omd1.Device(), ds1For2Encrypted) assert.NoError(t, err) ds1, err := mkh1.getOwnDeviceChainKeyForGroup(ctx, g) assert.NoError(t, err) initialCounter := ds1.Counter for i := 0; i < 1000; i++ { payloadRef, err := proto.Marshal(&protocoltypes.EncryptedMessage{Plaintext: []byte("Test payload 1")}) assert.NoError(t, err) envEncrypted, err := mkh1.SealEnvelope(ctx, g, payloadRef) assert.NoError(t, err) ds, err := mkh1.getDeviceChainKeyForGroupAndDevice(ctx, gPK, gc1DevicePubKey) if !assert.NoError(t, err) { t.Fatalf("failed at i = %d", i) } assert.Equal(t, ds.Counter, initialCounter+uint64(i+1)) env, headers, err := ((*secretStore)(nil)).OpenEnvelopeHeaders(envEncrypted, g) if !assert.NoError(t, err) { t.Fatalf("failed at i = %d", i) } payloadClr, err := mkh2.OpenEnvelopePayload(ctx, env, headers, gPK, omd2.Device(), cid.Undef) if !assert.NoError(t, err) { t.Fatalf("failed at i = %d", i) } if assert.NotNil(t, headers) && assert.NotNil(t, payloadClr) { devRaw, err := omd1.Device().Raw() assert.NoError(t, err) assert.Equal(t, headers.DevicePk, devRaw) payloadClrBytes, err := proto.Marshal(payloadClr) assert.NoError(t, err) assert.Equal(t, payloadRef, payloadClrBytes) } else { break } } } func mustDeviceChainKey(t testing.TB) func(ds *protocoltypes.DeviceChainKey, err error) *protocoltypes.DeviceChainKey { return func(ds *protocoltypes.DeviceChainKey, err error) *protocoltypes.DeviceChainKey { t.Helper() if err != nil { t.Fatal(err) } return ds } } func mustMessageHeaders(t testing.TB, omd OwnMemberDevice, counter uint64, payload []byte) *protocoltypes.MessageHeaders { t.Helper() pkB, err := omd.Device().Raw() if err != nil { t.Fatal(err) } sig, err := omd.DeviceSign(payload) if err != nil { t.Fatal(err) } return &protocoltypes.MessageHeaders{ Counter: counter, DevicePk: pkB, Sig: sig, } } func Test_EncryptMessagePayload(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() group, _, err := protocoltypes.NewGroupMultiMember() assert.NoError(t, err) mkh1, err := newInMemSecretStore(nil) assert.NoError(t, err) t.Cleanup(func() { mkh1.Close() }) mkh2, err := newInMemSecretStore(nil) assert.NoError(t, err) t.Cleanup(func() { mkh2.Close() }) omd1, err := mkh1.GetOwnMemberDeviceForGroup(group) assert.NoError(t, err) omd2, err := mkh2.GetOwnMemberDeviceForGroup(group) assert.NoError(t, err) encryptedDS1For1, err := mkh1.GetShareableChainKey(ctx, group, omd1.Member()) assert.NoError(t, err) encryptedDS1For2, err := mkh1.GetShareableChainKey(ctx, group, omd2.Member()) assert.NoError(t, err) encryptedDS2For2, err := mkh2.GetShareableChainKey(ctx, group, omd2.Member()) assert.NoError(t, err) gc1DevicePubKey := omd1.Device() gc2DevicePubKey := omd2.Device() err = mkh1.RegisterChainKey(ctx, group, gc1DevicePubKey, encryptedDS1For1) assert.NoError(t, err) err = mkh2.RegisterChainKey(ctx, group, gc2DevicePubKey, encryptedDS2For2) assert.NoError(t, err) ds1, err := mkh1.getOwnDeviceChainKeyForGroup(ctx, group) assert.NoError(t, err) initialCounter := ds1.Counter firstDeviceChainKey := append([]byte(nil), ds1.ChainKey...) payloadRef1 := []byte("ok, this is the first test") payloadRef2 := []byte("so, this is a second test") payloadRef3 := []byte("this will be posted many times") err = mkh2.RegisterChainKey(ctx, group, omd1.Device(), encryptedDS1For2) assert.NoError(t, err) gPK, err := group.GetPubKey() assert.NoError(t, err) assert.Equal(t, mustDeviceChainKey(t)(mkh1.getDeviceChainKeyForGroupAndDevice(ctx, gPK, omd1.Device())).ChainKey, firstDeviceChainKey) payloadEnc1, _, err := sealPayload(payloadRef1, mustDeviceChainKey(t)(mkh1.getDeviceChainKeyForGroupAndDevice(ctx, gPK, omd1.Device())), omd1.(*ownMemberDevice).device, group) assert.NoError(t, err) // secret is derived by sealEnvelope err = mkh1.deriveDeviceChainKey(ctx, group, omd1.Device()) assert.NoError(t, err) assert.NotEqual(t, hex.EncodeToString(payloadRef1), hex.EncodeToString(payloadEnc1)) // Messages are encrypted with DeviceChainKey.Counter // uint64 overflows to 0, which is the expected behaviour // Test with a wrong counter value payloadClr1, decryptInfo, err := mkh2.openPayload(ctx, cid.Undef, gPK, payloadEnc1, mustMessageHeaders(t, omd1, initialCounter+2, payloadRef1)) assert.Error(t, err) assert.Nil(t, decryptInfo) assert.Equal(t, "", string(payloadClr1)) // Test with a valid counter value, but no CID (so no cache) payloadClr1, decryptInfo, err = mkh2.openPayload(ctx, cid.Undef, gPK, payloadEnc1, mustMessageHeaders(t, omd1, initialCounter+1, payloadRef1)) assert.NoError(t, err) assert.Equal(t, string(payloadRef1), string(payloadClr1)) err = mkh2.postDecryptActions(ctx, decryptInfo, gPK, omd2.Device(), mustMessageHeaders(t, omd1, initialCounter+1, payloadRef1)) assert.NoError(t, err) ds, err := mkh1.getDeviceChainKeyForGroupAndDevice(ctx, gPK, omd1.Device()) assert.NoError(t, err) assert.Equal(t, ds.Counter, initialCounter+1) assert.NotEqual(t, ds.ChainKey, firstDeviceChainKey) payloadEnc2, _, err := sealPayload(payloadRef1, ds, omd1.(*ownMemberDevice).device, group) assert.NoError(t, err) err = mkh1.deriveDeviceChainKey(ctx, group, omd1.Device()) assert.NoError(t, err) // Ensure that encrypted message is not the same as the first message assert.NotEqual(t, hex.EncodeToString(payloadRef1), hex.EncodeToString(payloadEnc2)) assert.NotEqual(t, hex.EncodeToString(payloadEnc1), hex.EncodeToString(payloadEnc2)) payloadClr2, decryptInfo, err := mkh2.openPayload(ctx, cid.Undef, gPK, payloadEnc2, mustMessageHeaders(t, omd1, initialCounter+2, payloadRef1)) assert.NoError(t, err) err = mkh2.postDecryptActions(ctx, decryptInfo, gPK, omd2.Device(), mustMessageHeaders(t, omd1, initialCounter+2, payloadRef1)) assert.NoError(t, err) assert.Equal(t, string(payloadRef1), string(payloadClr2)) // Make sure that a message without a CID can't be decrypted twice payloadClr2, decryptInfo, err = mkh2.openPayload(ctx, cid.Undef, gPK, payloadEnc2, mustMessageHeaders(t, omd1, initialCounter+1, payloadRef1)) assert.Error(t, err) assert.Equal(t, "", string(payloadClr2)) ds, err = mkh1.getDeviceChainKeyForGroupAndDevice(ctx, gPK, omd1.Device()) assert.NoError(t, err) // Make sure that a message a CID can be decrypted twice payloadEnc3, _, err := sealPayload(payloadRef2, ds, omd1.(*ownMemberDevice).device, group) assert.NoError(t, err) err = mkh1.deriveDeviceChainKey(ctx, group, omd1.Device()) assert.NoError(t, err) dummyCID1, err := cid.Parse("QmbdQXQh9B2bWZgZJqfbjNPV5jGN2owbQ3vjeYsaDaCDqU") assert.NoError(t, err) dummyCID2, err := cid.Parse("Qmf8oj9wbfu73prNAA1cRQVDqA52gD5B3ApnYQQjcjffH4") assert.NoError(t, err) // Not decrypted message yet, wrong counter value payloadClr3, decryptInfo, err := mkh2.openPayload(ctx, dummyCID1, gPK, payloadEnc3, mustMessageHeaders(t, omd1, initialCounter+2, payloadRef2)) assert.Error(t, err) assert.Equal(t, "", string(payloadClr3)) payloadClr3, decryptInfo, err = mkh2.openPayload(ctx, dummyCID1, gPK, payloadEnc3, mustMessageHeaders(t, omd1, initialCounter+3, payloadRef2)) assert.NoError(t, err) assert.Equal(t, string(payloadRef2), string(payloadClr3)) err = mkh2.postDecryptActions(ctx, decryptInfo, gPK, omd2.Device(), mustMessageHeaders(t, omd1, initialCounter+3, payloadRef2)) assert.NoError(t, err) payloadClr3, decryptInfo, err = mkh2.openPayload(ctx, dummyCID1, gPK, payloadEnc3, mustMessageHeaders(t, omd1, initialCounter+3, payloadRef2)) assert.NoError(t, err) assert.Equal(t, string(payloadRef2), string(payloadClr3)) err = mkh2.postDecryptActions(ctx, decryptInfo, gPK, omd2.Device(), mustMessageHeaders(t, omd1, initialCounter+3, payloadRef2)) assert.NoError(t, err) // Wrong CID payloadClr3, decryptInfo, err = mkh2.openPayload(ctx, dummyCID2, gPK, payloadEnc3, mustMessageHeaders(t, omd1, initialCounter+3, payloadRef2)) assert.Error(t, err) assert.Equal(t, "", string(payloadClr3)) // Reused CID, wrong counter value payloadClr3, decryptInfo, err = mkh2.openPayload(ctx, dummyCID1, gPK, payloadEnc3, mustMessageHeaders(t, omd1, initialCounter+4, payloadRef2)) assert.Error(t, err) assert.Equal(t, "", string(payloadClr3)) massExpected := uint64(200) // Test appending 200 messages, to ensure new secrets are generated correctly for i := uint64(0); i < massExpected; i++ { ds, err = mkh1.getDeviceChainKeyForGroupAndDevice(ctx, gPK, omd1.Device()) assert.NoError(t, err) payloadEnc, _, err := sealPayload(payloadRef3, ds, omd1.(*ownMemberDevice).device, group) assert.NoError(t, err) err = mkh1.deriveDeviceChainKey(ctx, group, omd1.Device()) assert.NoError(t, err) ds, err = mkh1.getDeviceChainKeyForGroupAndDevice(ctx, gPK, omd1.Device()) assert.NoError(t, err) counter := ds.Counter payloadClr, decryptInfo, err := mkh2.openPayload(ctx, cid.Undef, gPK, payloadEnc, mustMessageHeaders(t, omd1, counter, payloadRef3)) if !assert.NoError(t, err) { t.Fatalf("failed at i = %d", i) } err = mkh2.postDecryptActions(ctx, decryptInfo, gPK, omd2.Device(), mustMessageHeaders(t, omd1, counter, payloadRef3)) assert.NoError(t, err) assert.Equal(t, string(payloadRef3), string(payloadClr)) } ds, err = mkh1.getDeviceChainKeyForGroupAndDevice(ctx, gPK, omd1.Device()) assert.NoError(t, err) assert.Equal(t, initialCounter+massExpected+3, ds.Counter) } func TestGetGroupForContact(t *testing.T) { privateKey, _, err := crypto.GenerateEd25519Key(crand.Reader) require.NoError(t, err) group, err := getGroupForContact(privateKey) require.NoError(t, err) require.Equal(t, group.GroupType, protocoltypes.GroupType_GroupTypeContact) require.Equal(t, len(group.PublicKey), 32) require.Equal(t, len(group.Secret), 32) } func TestGetKeysForGroupOfContact(t *testing.T) { privateKey, _, err := crypto.GenerateEd25519Key(crand.Reader) require.NoError(t, err) groupPrivateKey, groupSecretPrivateKey, err := getKeysForGroupOfContact(privateKey) require.NoError(t, err) require.NotNil(t, groupPrivateKey) require.NotNil(t, groupSecretPrivateKey) require.False(t, groupPrivateKey.Equals(groupSecretPrivateKey)) } ================================================ FILE: pkg/testutil/doc.go ================================================ // Package testutil contains testing helpers (logging, slow skipping). package testutil ================================================ FILE: pkg/testutil/example_test.go ================================================ package testutil_test ================================================ FILE: pkg/testutil/filters.go ================================================ package testutil import ( "testing" "google.golang.org/protobuf/proto" "berty.tech/weshnet/v2/pkg/protocoltypes" ) func TestFilterGroupMetadataPayloadSent(t *testing.T, events <-chan *protocoltypes.GroupMetadataEvent) []*protocoltypes.GroupMetadataPayloadSent { t.Helper() out := []*protocoltypes.GroupMetadataPayloadSent(nil) for evt := range events { if evt == nil { continue } if evt.Metadata.EventType != protocoltypes.EventType_EventTypeGroupMetadataPayloadSent { continue } m := &protocoltypes.GroupMetadataPayloadSent{} if err := proto.Unmarshal(evt.Event, m); err != nil { continue } out = append(out, m) } return out } ================================================ FILE: pkg/testutil/logging.go ================================================ package testutil import ( "flag" "fmt" "os" "strings" "sync" "testing" "github.com/stretchr/testify/assert" "go.uber.org/zap" "moul.io/zapring" "berty.tech/weshnet/v2/pkg/logutil" ) const defaultLoggingFilters = "info+:bty.test* error+:*,-ipfs*,-*.tyber" var ( logFilters = flag.String("log-filters", defaultLoggingFilters, "log namespaces") logFile = flag.String("log-file", "", "log to file") logFormat = flag.String("log-format", "color", "json, console, color") loggerInstance *zap.Logger loggerCleanup func() loggerInitOnce sync.Once loggerRing *zapring.Core ) func Logger(t testing.TB) (*zap.Logger, func()) { t.Helper() loggerInstance, _, loggerCleanup := LoggerWithRing(t) return loggerInstance, loggerCleanup } func LoggerWithRing(t testing.TB) (*zap.Logger, *zapring.Core, func()) { t.Helper() loggerInitOnce.Do(func() { if val := os.Getenv("BERTY_LOGFILTERS"); val != "" { *logFilters = val } if val := os.Getenv("BERTY_LOGFILE"); val != "" { *logFile = val } if val := os.Getenv("BERTY_LOGFORMAT"); val != "" { *logFormat = val } *logFilters = strings.ReplaceAll(*logFilters, ":default:", defaultLoggingFilters) var err error loggerRing = zapring.New(10 * 1024 * 1024) loggerInstance, loggerCleanup, err = logutil.NewLogger( logutil.NewStdStream(*logFilters, *logFormat, *logFile), logutil.NewRingStream(*logFilters, *logFormat, loggerRing), ) if !assert.NoError(t, err) { loggerInstance = zap.NewNop() loggerCleanup = func() {} } }) return loggerInstance, loggerRing, loggerCleanup } func LogTree(t *testing.T, log string, indent int, title bool, args ...any) { t.Helper() if os.Getenv("SHOW_LOG_TREES") != "1" { return } if len(args) > 0 { log = fmt.Sprintf(log, args...) } if !title { log = "└── " + log } for i := 0; i < indent; i++ { log = "│ " + log } t.Log(log) } ================================================ FILE: pkg/testutil/require.go ================================================ package testutil import ( "io" "testing" "github.com/stretchr/testify/require" ) func Close(t *testing.T, closer io.Closer) { t.Helper() require.NoError(t, closer.Close()) } ================================================ FILE: pkg/testutil/skip.go ================================================ package testutil import ( "fmt" "os" "strings" "testing" ) // Stability level enum type Stability string const ( Stable Stability = "stable" Flappy Stability = "flappy" Broken Stability = "broken" AnyStability Stability = "any" ) // Speed level enum type Speed string const ( Fast Speed = "fast" Slow Speed = "slow" AnySpeed Speed = "any" ) // RacePolicy enum type RacePolicy string const ( SkipIfRace RacePolicy = "skip-if-race" RunIfRace RacePolicy = "run-if-race" ) // Default levels const ( defaultStabilityFilter Stability = Stable defaultSpeedFilter Speed = AnySpeed ) var ( enabledStability = map[Stability]bool{} enabledSpeed = map[Speed]bool{} envParsed = false ) func parseEnv() { // Get stability filters stabFilter := os.Getenv("TEST_STABILITY") if stabFilter == "" { stabFilter = string(defaultStabilityFilter) } for _, level := range strings.Split(stabFilter, ",") { switch Stability(level) { case Stable, Flappy, Broken: enabledStability[Stability(level)] = true case AnyStability: enabledStability[Stable] = true enabledStability[Flappy] = true enabledStability[Broken] = true default: panic(fmt.Sprintf("invalid stability level: %q", level)) } } // Get speed filters speedFilter := os.Getenv("TEST_SPEED") if speedFilter == "" { speedFilter = string(defaultSpeedFilter) } for _, level := range strings.Split(speedFilter, ",") { switch Speed(level) { case Slow, Fast: enabledSpeed[Speed(level)] = true case AnySpeed: enabledSpeed[Slow] = true enabledSpeed[Fast] = true default: panic(fmt.Sprintf("invalid speed level: %q", level)) } } envParsed = true } func FilterStability(t *testing.T, stability Stability) { t.Helper() if !envParsed { parseEnv() } if !enabledStability[stability] { t.Skipf("skip test with %q stability", stability) } } func FilterSpeed(t *testing.T, speed Speed) { t.Helper() if !envParsed { parseEnv() } if !enabledSpeed[speed] { t.Skipf("skip test with %q speed", speed) } } func FilterStabilityAndSpeed(t *testing.T, stability Stability, speed Speed) { FilterStability(t, stability) FilterSpeed(t, speed) } ================================================ FILE: pkg/testutil/skip_norace.go ================================================ //go:build !race package testutil import "testing" func FilterRace(t *testing.T, race RacePolicy) { t.Helper() if race == RunIfRace { t.Skip("skipping, this test can only run with the '-race' flag") } } ================================================ FILE: pkg/testutil/skip_race.go ================================================ //go:build race package testutil import "testing" func FilterRace(t *testing.T, race RacePolicy) { t.Helper() if race == SkipIfRace { t.Skip("skipping, this test can only run without the '-race' flag") } } ================================================ FILE: pkg/testutil/skip_test.go ================================================ package testutil_test import ( "testing" "berty.tech/weshnet/v2/pkg/testutil" ) func TestFilterRace(t *testing.T) { t.Run("always-run", func(t *testing.T) {}) t.Run("skip-if-race", func(t *testing.T) { testutil.FilterRace(t, testutil.SkipIfRace) }) t.Run("run-if-race", func(t *testing.T) { testutil.FilterRace(t, testutil.RunIfRace) }) t.Run("always-skip", func(t *testing.T) { t.Skip() }) } ================================================ FILE: pkg/tinder/driver.go ================================================ package tinder import ( "context" "fmt" "time" "github.com/libp2p/go-libp2p/core/discovery" "github.com/libp2p/go-libp2p/core/peer" ) var ErrNotSupported = fmt.Errorf("not supported") var _ discovery.Discovery = (IDriver)(nil) type IDriver interface { Name() string Subscribe(ctx context.Context, topic string, opts ...discovery.Option) (<-chan peer.AddrInfo, error) Unregister(ctx context.Context, topic string, opts ...discovery.Option) error // discovery Advertise(ctx context.Context, ns string, opts ...discovery.Option) (time.Duration, error) FindPeers(ctx context.Context, ns string, opts ...discovery.Option) (<-chan peer.AddrInfo, error) } ================================================ FILE: pkg/tinder/driver_discovery.go ================================================ package tinder import ( "context" "time" "github.com/libp2p/go-libp2p/core/discovery" "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/routing" disc_routing "github.com/libp2p/go-libp2p/p2p/discovery/routing" ) type DiscoveryDriver struct { name string discovery.Discovery } func NewRoutingDiscoveryDriver(name string, routing routing.Routing) IDriver { disc := disc_routing.NewRoutingDiscovery(routing) return NewDiscoveryDriver(name, disc) } func NewDiscoveryDriver(name string, disc discovery.Discovery) IDriver { return &DiscoveryDriver{name, disc} } // discovery advertise func (d *DiscoveryDriver) Advertise(ctx context.Context, topic string, opts ...discovery.Option) (time.Duration, error) { return d.Discovery.Advertise(ctx, topic, opts...) } // discovery find peers func (d *DiscoveryDriver) FindPeers(ctx context.Context, topic string, opts ...discovery.Option) (<-chan peer.AddrInfo, error) { return d.Discovery.FindPeers(ctx, topic, opts...) } func (d *DiscoveryDriver) Subscribe(_ context.Context, _ string, _ ...discovery.Option) (<-chan peer.AddrInfo, error) { return nil, ErrNotSupported } func (d *DiscoveryDriver) Unregister(_ context.Context, _ string, _ ...discovery.Option) error { return ErrNotSupported } func (d *DiscoveryDriver) Name() string { return d.name } ================================================ FILE: pkg/tinder/driver_localdiscovery.go ================================================ package tinder import ( "container/list" "context" "encoding/hex" "fmt" "math/rand" "slices" "sync" "time" "github.com/libp2p/go-libp2p/core/discovery" "github.com/libp2p/go-libp2p/core/event" "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/network" "github.com/libp2p/go-libp2p/core/peer" protocol "github.com/libp2p/go-libp2p/core/protocol" "github.com/libp2p/go-libp2p/p2p/host/eventbus" ma "github.com/multiformats/go-multiaddr" manet "github.com/multiformats/go-multiaddr/net" "go.uber.org/zap" nearby "berty.tech/weshnet/v2/pkg/androidnearby" ble "berty.tech/weshnet/v2/pkg/ble-driver" "berty.tech/weshnet/v2/pkg/logutil" mc "berty.tech/weshnet/v2/pkg/multipeer-connectivity-driver" "berty.tech/weshnet/v2/pkg/protoio" ) const ( // LocalDiscoveryName is the name of the localdiscovery driver LocalDiscoveryName = "localdisc" recProtocolID = protocol.ID("wesh/p2p/localrecord") minTTL = 7200 * time.Second maxLimit = 1000 ) type LocalDiscovery struct { rootctx context.Context rootcancel context.CancelFunc h host.Host logger *zap.Logger recs map[string] /* topic */ time.Time muRecs sync.RWMutex caches map[string] /* topic */ *linkedCache muCaches sync.RWMutex } type linkedCache struct { sync.Locker recs map[peer.ID]*recCache queue *list.List cond *sync.Cond } type recCache struct { topic string peer *peer.AddrInfo expire time.Time } func newLinkedCache() *linkedCache { locker := sync.Mutex{} return &linkedCache{ queue: list.New(), recs: make(map[peer.ID]*recCache), Locker: &locker, cond: sync.NewCond(&locker), } } func NewLocalDiscovery(logger *zap.Logger, host host.Host, _ *rand.Rand) (*LocalDiscovery, error) { ctx, cancel := context.WithCancel(context.Background()) ld := &LocalDiscovery{ rootctx: ctx, rootcancel: cancel, logger: logger.Named("localdisc"), h: host, recs: make(map[string]time.Time), caches: make(map[string]*linkedCache), } host.SetStreamHandler(recProtocolID, ld.handleStream) if err := ld.monitorConnection(ctx); err != nil { return nil, fmt.Errorf("unable to monitor connection: %w", err) } return ld, nil } func (ld *LocalDiscovery) Close() error { ld.rootcancel() return nil } func (ld *LocalDiscovery) Advertise(ctx context.Context, cid string, opts ...discovery.Option) (time.Duration, error) { ld.logger.Debug("advertise record", logutil.PrivateString("ns", cid)) // Get options var options discovery.Options err := options.Apply(opts...) if err != nil { return minTTL, err } limit := options.Limit if limit == 0 || limit > maxLimit { limit = maxLimit } ttl := max(options.Ttl, minTTL) expire := time.Now().Add(ttl) ld.muRecs.Lock() _, exist := ld.recs[cid] if !exist && len(ld.recs) > limit { ld.muRecs.Unlock() return minTTL, fmt.Errorf("unable to add record, reached limit of %d", limit) } ld.recs[cid] = expire ld.muRecs.Unlock() ld.logger.Debug("advertise", zap.String("ns", hex.EncodeToString([]byte(cid)))) // send it to already connected proximity peers records := []*Record{{Cid: cid, Expire: expire.Unix()}} ld.sendRecordsToProximityPeers(ctx, &Records{Records: records}) return ttl, nil } func (ld *LocalDiscovery) FindPeers(_ context.Context, cid string, opts ...discovery.Option) (<-chan peer.AddrInfo, error) { // Get options var options discovery.Options err := options.Apply(opts...) if err != nil { return nil, err } limit := options.Limit if limit == 0 || limit > maxLimit { limit = maxLimit } ld.muCaches.RLock() defer ld.muCaches.RUnlock() var out chan peer.AddrInfo if cache, ok := ld.caches[cid]; ok { cache.Lock() size := min(len(cache.recs), limit) out = make(chan peer.AddrInfo, size) // make the channel for _, rec := range cache.recs { out <- *rec.peer size-- if size == 0 { break } } cache.Unlock() } else { out = make(chan peer.AddrInfo) // make the channel } close(out) return out, nil } func (ld *LocalDiscovery) Subscribe(ctx context.Context, cid string, opts ...discovery.Option) (<-chan peer.AddrInfo, error) { ld.logger.Debug("starting subscribe", logutil.PrivateString("ns", cid)) cpeer := make(chan peer.AddrInfo) // Get options var options discovery.Options err := options.Apply(opts...) if err != nil { close(cpeer) return cpeer, err } limit := options.Limit if limit == 0 || limit > maxLimit { limit = maxLimit } ld.muCaches.Lock() cache, ok := ld.caches[cid] if !ok { cache = newLinkedCache() ld.caches[cid] = cache } ld.muCaches.Unlock() ctx, cancel := context.WithCancel(ctx) go func() { <-ctx.Done() cancel() cache.cond.Broadcast() }() cache.Lock() current := cache.queue.Front() cache.Unlock() counter := 0 go func() { cache.Lock() for ctx.Err() == nil && counter <= limit { if current == nil { // unlock the mutex // wait for new elements in the list cache.cond.Wait() current = cache.queue.Back() continue } next := current.Next() rec := current.Value.(*recCache) if time.Now().After(rec.expire) { ld.logger.Debug("receiving expired record", logutil.PrivateString("ns", cid), zap.Duration("since", time.Since(rec.expire))) delete(cache.recs, rec.peer.ID) cache.queue.Remove(current) } else { ld.logger.Debug("receiving record", logutil.PrivateString("ns", cid)) select { case cpeer <- *rec.peer: counter++ case <-ctx.Done(): } } // move our pointer forward current = next } close(cpeer) cache.Unlock() ld.logger.Debug("find peers ended", logutil.PrivateString("ns", cid), zap.Error(ctx.Err())) }() return cpeer, nil } func (ld *LocalDiscovery) getLocalReccord() *Records { records := []*Record{} now := time.Now() ld.muRecs.Lock() for cid, expire := range ld.recs { // if expired remove from cache if now.After(expire) { delete(ld.recs, cid) continue } records = append(records, &Record{ Cid: cid, Expire: expire.Unix(), }) } ld.muRecs.Unlock() return &Records{Records: records} } func (ld *LocalDiscovery) Unregister(_ context.Context, cid string, _ ...discovery.Option) error { ld.muRecs.Lock() delete(ld.recs, cid) ld.muRecs.Unlock() return nil } func (ld *LocalDiscovery) sendRecordsToProximityPeers(ctx context.Context, records *Records) { conns := ld.h.Network().Conns() wg := sync.WaitGroup{} for _, c := range conns { if manet.IsPrivateAddr(c.RemoteMultiaddr()) || isProximityProtocol(c.RemoteMultiaddr()) { wg.Add(1) go func(c network.Conn) { ld.logger.Debug("sending records to peer", zap.Stringer("peer", c.RemotePeer())) if err := ld.sendRecordsTo(ctx, c.RemotePeer(), records); err != nil { ld.logger.Warn("unable to send records to newly connected peer", zap.Error(err), zap.Stringer("peer", c.RemotePeer())) } wg.Done() }(c) } } wg.Wait() } // Called when is a stream is opened by a remote peer // Receive remote peer cache and put it in our local cache func (ld *LocalDiscovery) handleStream(s network.Stream) { defer s.Reset() // nolint:errcheck reader := protoio.NewDelimitedReader(s, 2048) records := Records{} if err := reader.ReadMsg(&records); err != nil { ld.logger.Error("handleStream receive an invalid local record", zap.Error(err)) return } info := peer.AddrInfo{ ID: s.Conn().RemotePeer(), Addrs: []ma.Multiaddr{s.Conn().RemoteMultiaddr()}, } // fill the remote cache for _, record := range records.Records { expire := time.Unix(record.Expire, 0) ld.logger.Debug("saving remote record in cache", logutil.PrivateString("remoteID", s.Conn().RemotePeer().String()), logutil.PrivateString("CID", record.Cid), zap.Time("expire", expire)) ld.muCaches.Lock() cache, ok := ld.caches[record.Cid] if !ok { cache = newLinkedCache() ld.caches[record.Cid] = cache } ld.muCaches.Unlock() rec := &recCache{ peer: &info, topic: record.Cid, expire: expire, } // update the given cache record and signal to other routine that we got an update cache.Lock() if cacheRec, ok := cache.recs[info.ID]; ok { cacheRec.peer = &info cacheRec.expire = expire } else { cache.recs[info.ID] = rec cache.queue.PushBack(rec) } cache.cond.Broadcast() cache.Unlock() } } func (ld *LocalDiscovery) sendRecordsTo(ctx context.Context, p peer.ID, records *Records) error { s, err := ld.h.NewStream(ctx, p, recProtocolID) if err != nil { return fmt.Errorf("unable to create stream: %w", err) } defer s.Close() pbw := protoio.NewDelimitedWriter(s) if err := pbw.WriteMsg(records); err != nil { return fmt.Errorf("write error: %w", err) } return nil } func isProximityProtocol(addr ma.Multiaddr) bool { for _, p := range addr.Protocols() { switch p.Code { case ble.ProtocolCode, mc.ProtocolCode, nearby.ProtocolCode: return true default: } } return false } func (ld *LocalDiscovery) handleConnection(ctx context.Context, p peer.ID) { if p == ld.h.ID() { return } // check if we use a proximity addrs with this peer conns := ld.h.Network().ConnsToPeer(p) for _, conn := range conns { if manet.IsPrivateAddr(conn.RemoteMultiaddr()) || isProximityProtocol(conn.RemoteMultiaddr()) { go func() { records := ld.getLocalReccord() if err := ld.sendRecordsTo(ctx, p, records); err != nil { ld.logger.Warn("unable to send local record", logutil.PrivateString("peer", p.String()), zap.Int("records", len(records.Records)), zap.Error(err)) } else { ld.logger.Info("send topics to local peer", logutil.PrivateString("peer", conn.RemotePeer().String())) } }() return } } } func (ld *LocalDiscovery) monitorConnection(ctx context.Context) error { sub, err := ld.h.EventBus().Subscribe([]any{ new(event.EvtPeerConnectednessChanged), new(event.EvtPeerProtocolsUpdated), }, eventbus.Name("weshnet/tinder/monitor-connection")) if err != nil { return fmt.Errorf("unable to subscribe to `EvtPeerConnectednessChanged`: %w", err) } // check already connected peers for _, p := range ld.h.Peerstore().Peers() { if ld.h.Network().Connectedness(p) == network.Connected { ld.handleConnection(ctx, p) } } go func() { defer sub.Close() for { var e any select { case e = <-sub.Out(): case <-ctx.Done(): return } switch evt := e.(type) { case event.EvtPeerConnectednessChanged: // send record to connected peer only if evt.Connectedness == network.Connected { ld.handleConnection(ctx, evt.Peer) } case event.EvtPeerProtocolsUpdated: if slices.Contains(evt.Added, recProtocolID) { ld.handleConnection(ctx, evt.Peer) } } } }() return nil } func (ld *LocalDiscovery) Name() string { return LocalDiscoveryName } ================================================ FILE: pkg/tinder/driver_localdiscovery_test.go ================================================ package tinder import ( "context" "math/rand" "testing" "time" mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" "github.com/stretchr/testify/require" "berty.tech/weshnet/v2/pkg/testutil" ) func TestServiceLocalDiscorvery(t *testing.T) { ctx := context.Background() mn := mocknet.New() defer mn.Close() logger, cleanup := testutil.Logger(t) defer cleanup() p1 := genLocalPeer(t, mn) p2 := genLocalPeer(t, mn) err := mn.LinkAll() require.NoError(t, err) disc1, err := NewLocalDiscovery(logger, p1, rand.New(rand.NewSource(rand.Int63()))) require.NoError(t, err) disc2, err := NewLocalDiscovery(logger, p2, rand.New(rand.NewSource(rand.Int63()))) require.NoError(t, err) s1, err := NewService(p1, logger, disc1) require.NoError(t, err) s2, err := NewService(p2, logger, disc2) require.NoError(t, err) const topic = "test_topic" s1.StartAdvertises(ctx, topic) // try a first lookup, should find nothing { out := s2.FindPeers(ctx, topic) peers := testPeersChanToSlice(t, out) require.Len(t, peers, 0, "no peer should be available") } { // start a subscribe and wait for connection sub := s2.Subscribe(topic) defer sub.Close() err = mn.ConnectAllButSelf() require.NoError(t, err) p, err := testWaitForPeers(t, sub.Out(), time.Second*5) require.NoError(t, err) require.Equal(t, p1.ID(), p.ID) require.Equal(t, p1.Addrs(), p.Addrs) } // try a lookup again, this time we should have some peers { out := s2.FindPeers(ctx, topic) p, err := testWaitForPeers(t, out, time.Second*5) require.NoError(t, err) require.Equal(t, p1.ID(), p.ID) require.Equal(t, p1.Addrs(), p.Addrs) } } func TestServiceLocalDiscorveryBeforeProtocolRegister(t *testing.T) { const topic = "test_topic" ctx, cancel := context.WithCancel(context.Background()) defer cancel() mn := mocknet.New() defer mn.Close() logger, cleanup := testutil.Logger(t) defer cleanup() p1 := genLocalPeer(t, mn) p2 := genLocalPeer(t, mn) err := mn.LinkAll() require.NoError(t, err) disc1, err := NewLocalDiscovery(logger, p1, rand.New(rand.NewSource(rand.Int63()))) require.NoError(t, err) // create service for peer 1 s1, err := NewService(p1, logger, disc1) require.NoError(t, err) // start advertising s1.StartAdvertises(ctx, topic) // connect both peer BEFORE registering local discovery protocol for peer 2 err = mn.ConnectAllButSelf() require.NoError(t, err) // let some time to peers to connect and trigger protocol exchange time.Sleep(time.Millisecond * 200) // register p2 local discovery disc2, err := NewLocalDiscovery(logger, p2, rand.New(rand.NewSource(rand.Int63()))) require.NoError(t, err) s2, err := NewService(p2, logger, disc2) require.NoError(t, err) // start subscribe and wait for connection sub := s2.Subscribe(topic) defer sub.Close() // pull to fetch current peers on the topic sub.Pull() select { case p := <-sub.Out(): require.Equal(t, p.ID, p1.ID()) case <-time.After(time.Second * 5): require.FailNow(t, "unable to wait for peer on local discovery") } } ================================================ FILE: pkg/tinder/driver_mock.go ================================================ package tinder import ( "context" "fmt" "slices" "sync" "time" "github.com/libp2p/go-libp2p/core/discovery" "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/peer" ) type MockDriverServer struct { pc *peersCache topics map[string] /* topic */ map[peer.ID]time.Time mx sync.RWMutex } func NewMockDriverServer() *MockDriverServer { return &MockDriverServer{ topics: make(map[string]map[peer.ID]time.Time), pc: newPeerCache(), } } func (s *MockDriverServer) Advertise(topic string, info peer.AddrInfo, ttl time.Duration) { s.mx.Lock() expires, ok := s.topics[topic] if !ok { expires = map[peer.ID]time.Time{} s.topics[topic] = expires } expires[info.ID] = time.Now().Add(ttl) s.mx.Unlock() s.pc.UpdatePeer(topic, info) } func (s *MockDriverServer) FindPeers(topic string, limit int) <-chan peer.AddrInfo { s.mx.Lock() defer s.mx.Unlock() peers := s.pc.GetPeersForTopics(topic) if len(peers) < limit { limit = len(peers) } expires, ok := s.topics[topic] if !ok { expires = map[peer.ID]time.Time{} s.topics[topic] = expires } out := make(chan peer.AddrInfo, limit) for i := 0; i < limit; i++ { peer := peers[i] if expire, ok := expires[peer.ID]; ok { if time.Now().Before(expire) { out <- peer } else { delete(expires, peer.ID) } } } close(out) return out } func (s *MockDriverServer) Unregister(ctx context.Context, topic string, p peer.ID) { s.mx.Lock() s.pc.RemoveFromCache(ctx, topic, p) if expires, ok := s.topics[topic]; ok { delete(expires, p) } s.mx.Unlock() } func (s *MockDriverServer) Exist(topic string, _ peer.ID) (ok bool) { peers := s.pc.GetPeersForTopics(topic) return len(peers) == 1 } func (s *MockDriverServer) Subscribe(ctx context.Context, topic string, buffsize int) <-chan peer.AddrInfo { s.mx.Lock() defer s.mx.Unlock() start := time.Now() peers := s.pc.GetPeersForTopics(topic) knownPeers := make(PeersUpdate) for _, p := range peers { knownPeers[p.ID] = start } out := make(chan peer.AddrInfo, buffsize) go func() { for { // Wait until `PeersCache` differ from `peerCache` peers status updated, ok := s.pc.WaitForPeerUpdate(ctx, topic, knownPeers) if !ok { break // context has expired } // send peers for _, peer := range s.pc.GetPeers(updated...) { out <- peer } } // we're done here, close the channel and decrement close(out) }() return out } func (s *MockDriverServer) WaitForPeer(topic string, p peer.ID, timeout time.Duration) (err error) { ctx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() knownPeers := make(PeersUpdate) for { // Wait until `PeersCache` differ from `peerCache` peers status updated, expired := s.pc.WaitForPeerUpdate(ctx, topic, knownPeers) if !expired { return fmt.Errorf("timeout while waiting for: %s", p.String()) } // send peers if slices.Contains(updated, p) { return nil } } } func (s *MockDriverServer) Client(h host.Host) IDriver { return &MockIDriverClient{h: h, serv: s} } var _ IDriver = (*MockIDriverClient)(nil) type MockIDriverClient struct { h host.Host serv *MockDriverServer } func (s *MockIDriverClient) Name() string { return "mock" } func (s *MockIDriverClient) FindPeers(_ context.Context, topic string, opts ...discovery.Option) (<-chan peer.AddrInfo, error) { var options discovery.Options err := options.Apply(opts...) if err != nil { return nil, err } if options.Ttl == 0 { options.Limit = 1000 } return s.serv.FindPeers(topic, options.Limit), nil } func (s *MockIDriverClient) Advertise(_ context.Context, topic string, opts ...discovery.Option) (time.Duration, error) { var options discovery.Options err := options.Apply(opts...) if err != nil { return 0, err } if options.Ttl == 0 { options.Ttl = time.Second * 10 } info := s.h.Peerstore().PeerInfo(s.h.ID()) s.serv.Advertise(topic, info, options.Ttl) return options.Ttl, nil } func (s *MockIDriverClient) Subscribe(ctx context.Context, topic string, opts ...discovery.Option) (<-chan peer.AddrInfo, error) { var options discovery.Options err := options.Apply(opts...) if err != nil { return nil, err } v := options.Other[optionSubscribeBufferSize] var buffsize int var ok bool if buffsize, ok = v.(int); !ok || buffsize == 0 { buffsize = 16 } out := s.serv.Subscribe(ctx, topic, buffsize) return out, nil } func (s *MockIDriverClient) Unregister(ctx context.Context, topic string, _ ...discovery.Option) error { s.serv.Unregister(ctx, topic, s.h.ID()) return nil } type discOption string const ( optionSubscribeBufferSize discOption = "tinder_subsize" ) func MockBufferSize(size int) discovery.Option { return func(opts *discovery.Options) error { if opts.Other == nil { opts.Other = make(map[any]any) } opts.Other[optionSubscribeBufferSize] = size return nil } } ================================================ FILE: pkg/tinder/driver_mock_test.go ================================================ package tinder import ( "context" "testing" "time" "github.com/libp2p/go-libp2p/core/peer" mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestMockAdvertise(t *testing.T) { const topic = "test_topic'" ctx, cancel := context.WithCancel(context.Background()) defer cancel() mn := mocknet.New() defer mn.Close() p1, err := mn.GenPeer() require.NoError(t, err) server := NewMockDriverServer() client := server.Client(p1) info1 := p1.Peerstore().PeerInfo(p1.ID()) require.False(t, server.Exist(topic, info1.ID)) ttl, err := client.Advertise(ctx, topic) require.NoError(t, err) require.Greater(t, ttl, time.Duration(0)) require.True(t, server.Exist(topic, info1.ID)) } func TestMockFindPeers(t *testing.T) { const topic = "test_topic'" ctx, cancel := context.WithCancel(context.Background()) defer cancel() mn := mocknet.New() defer mn.Close() p1, err := mn.GenPeer() require.NoError(t, err) p2, err := mn.GenPeer() require.NoError(t, err) server := NewMockDriverServer() client1 := server.Client(p1) client2 := server.Client(p2) info1 := p1.Peerstore().PeerInfo(p1.ID()) server.Advertise(topic, info1, time.Minute) for _, client := range []IDriver{client1, client2} { out, err := client.FindPeers(ctx, topic) require.NoError(t, err) var peers []peer.AddrInfo for p := range out { peers = append(peers, p) } require.Len(t, peers, 1) assert.Equal(t, info1, peers[0]) } info2 := p2.Peerstore().PeerInfo(p2.ID()) server.Advertise(topic, info2, time.Minute) for _, client := range []IDriver{client1, client2} { out, err := client.FindPeers(ctx, topic) require.NoError(t, err) var peers []peer.AddrInfo for p := range out { peers = append(peers, p) } require.Len(t, peers, 2) } } func TestMockSubscribe(t *testing.T) { const topic = "test_topic'" mn := mocknet.New() defer mn.Close() p1, err := mn.GenPeer() require.NoError(t, err) p2, err := mn.GenPeer() require.NoError(t, err) info2 := p1.Peerstore().PeerInfo(p2.ID()) p3, err := mn.GenPeer() require.NoError(t, err) info3 := p1.Peerstore().PeerInfo(p3.ID()) server := NewMockDriverServer() client1 := server.Client(p1) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() out, err := client1.Subscribe(ctx, topic, MockBufferSize(2)) require.NoError(t, err) server.Advertise(topic, info2, time.Minute) server.Advertise(topic, info3, time.Minute) peers := map[peer.ID]peer.AddrInfo{} for i := 0; i < 2; i++ { peer := <-out peers[peer.ID] = peer } p2, ok := peers[info2.ID] require.True(t, ok) require.Equal(t, info2, p2) p3, ok := peers[info3.ID] require.True(t, ok) require.Equal(t, info3, p3) cancel() // should be close if context expire require.Eventually(t, func() bool { select { case <-out: return true default: return false } }, time.Second, time.Millisecond*10) } } func TestMockUnregister(t *testing.T) { const topic = "test_topic'" ctx, cancel := context.WithCancel(context.Background()) defer cancel() mn := mocknet.New() defer mn.Close() p1, err := mn.GenPeer() require.NoError(t, err) server := NewMockDriverServer() client := server.Client(p1) info1 := p1.Peerstore().PeerInfo(p1.ID()) server.Advertise(topic, info1, time.Minute) require.True(t, server.Exist(topic, info1.ID)) err = client.Unregister(ctx, topic) require.NoError(t, err) require.False(t, server.Exist(topic, info1.ID)) } ================================================ FILE: pkg/tinder/driver_rdvp.go ================================================ // from https://github.com/berty/go-libp2p-rendezvous/blob/implement-spec/discovery.go package tinder import ( "context" "fmt" "math" mrand "math/rand" "sync" "time" p2p_rp "github.com/berty/go-libp2p-rendezvous" "github.com/libp2p/go-libp2p/core/discovery" "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/peer" "go.uber.org/zap" ) type rendezvousDiscovery struct { logger *zap.Logger rp p2p_rp.RendezvousPoint synccls []p2p_rp.RendezvousSyncClient peerCache map[string]*rpCache peerCacheMux sync.RWMutex rng *mrand.Rand rngMux sync.Mutex selfID peer.ID } type rpCache struct { recs map[peer.ID]*rpRecord cookie []byte mux sync.Mutex } type rpRecord struct { peer peer.AddrInfo expire int64 } func NewRendezvousDiscovery(logger *zap.Logger, host host.Host, rdvPeer peer.ID, factory p2p_rp.AddrsFactory, rng *mrand.Rand, emitters ...p2p_rp.RendezvousSyncClient) IDriver { rp := p2p_rp.NewRendezvousPoint(host, rdvPeer, p2p_rp.ClientWithAddrsFactory(factory)) return &rendezvousDiscovery{ logger: logger.Named("tinder/rdvp"), rp: rp, rng: rng, synccls: emitters, peerCache: make(map[string]*rpCache), selfID: host.ID(), } } func (c *rendezvousDiscovery) Advertise(ctx context.Context, topic string, opts ...discovery.Option) (time.Duration, error) { // Get options var options discovery.Options err := options.Apply(opts...) if err != nil { return 0, err } ttl := options.Ttl var ttlSeconds int if ttl == 0 { ttlSeconds = 7200 } else { ttlSeconds = int(math.Round(ttl.Seconds())) } rttl, err := c.rp.Register(ctx, topic, ttlSeconds) if err != nil { return 0, err } return rttl, nil } func (c *rendezvousDiscovery) FindPeers(ctx context.Context, topic string, opts ...discovery.Option) (<-chan peer.AddrInfo, error) { // Get options var options discovery.Options err := options.Apply(opts...) if err != nil { return nil, err } const maxLimit = 1000 limit := options.Limit if limit == 0 || limit > maxLimit { limit = maxLimit } return c.findPeers(ctx, topic, limit) } func (c *rendezvousDiscovery) findPeers(ctx context.Context, topic string, limit int) (<-chan peer.AddrInfo, error) { // Get cached peers var err error c.peerCacheMux.RLock() cache, ok := c.peerCache[topic] c.peerCacheMux.RUnlock() if !ok { c.peerCacheMux.Lock() cache, ok = c.peerCache[topic] if !ok { cache = &rpCache{recs: make(map[peer.ID]*rpRecord)} c.peerCache[topic] = cache } c.peerCacheMux.Unlock() } cache.mux.Lock() defer cache.mux.Unlock() // Remove all expired entries from cache currentTime := time.Now().Unix() newCacheSize := len(cache.recs) for p := range cache.recs { rec := cache.recs[p] if rec.expire < currentTime { newCacheSize-- delete(cache.recs, p) } } cookie := cache.cookie // Discover new records if we don't have enough if newCacheSize < limit { // TODO: Should we return error even if we have valid cached results? var regs []p2p_rp.Registration var newCookie []byte if regs, newCookie, err = c.rp.Discover(ctx, topic, limit, cookie); err == nil { for _, reg := range regs { rec := &rpRecord{peer: reg.Peer, expire: int64(reg.Ttl) + currentTime} cache.recs[rec.peer.ID] = rec } cache.cookie = newCookie } else { err = fmt.Errorf("unable to run discover: %w", err) } } // Randomize and fill channel with available records count := min(limit, len(cache.recs)) chPeer := make(chan peer.AddrInfo, count) c.rngMux.Lock() perm := c.rng.Perm(len(cache.recs))[0:count] c.rngMux.Unlock() permSet := make(map[int]int) for i, v := range perm { permSet[v] = i } sendLst := make([]*peer.AddrInfo, count) iter := 0 for k := range cache.recs { if sendIndex, ok := permSet[iter]; ok { sendLst[sendIndex] = &cache.recs[k].peer } iter++ } for _, send := range sendLst { chPeer <- *send } close(chPeer) return chPeer, err } func (c *rendezvousDiscovery) Subscribe(ctx context.Context, topic string, _ ...discovery.Option) (<-chan peer.AddrInfo, error) { ch, err := c.getSubscribtionForTopic(ctx, topic) return ch, err } func (c *rendezvousDiscovery) Unregister(ctx context.Context, topic string, _ ...discovery.Option) error { c.peerCacheMux.RLock() cache, ok := c.peerCache[topic] if ok { cache.mux.Lock() delete(cache.recs, c.selfID) cache.mux.Unlock() } c.peerCacheMux.RUnlock() return c.rp.Unregister(ctx, topic) } func (c *rendezvousDiscovery) Name() string { return "rdvp" } func (c *rendezvousDiscovery) getSubscribtionForTopic(ctx context.Context, topic string) (<-chan peer.AddrInfo, error) { if len(c.synccls) > 0 { return c.rp.DiscoverSubscribe(ctx, topic, c.synccls) } ch := make(chan peer.AddrInfo, 16) creg, err := c.rp.DiscoverAsync(ctx, topic) if err != nil { return nil, fmt.Errorf("unable to start discovery async: %w", err) } go func() { defer close(ch) for reg := range creg { ch <- reg.Peer } }() return ch, nil } // nolint:gochecknoinits func init() { p2p_rp.DiscoverAsyncInterval = time.Second * 30 } ================================================ FILE: pkg/tinder/driver_service_test.go ================================================ package tinder import ( "context" "fmt" "math/rand" "testing" "time" rendezvous "github.com/berty/go-libp2p-rendezvous" dht "github.com/libp2p/go-libp2p-kad-dht" "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/peer" mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" "github.com/stretchr/testify/require" "go.uber.org/zap" "berty.tech/weshnet/v2/pkg/testutil" ) type testMakeDriver = func(t *testing.T, logger *zap.Logger, p host.Host) IDriver func TestMultipleDriversSubscribe(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() mn := mocknet.New() defer mn.Close() targetRdvp, _ := makeRendezvousService(t, mn) cases := []struct { Name string Makes []testMakeDriver }{ {"mocked driver", []testMakeDriver{ testMakeMockedDriverFactory(NewMockDriverServer()), }}, {"localdisc driver", []testMakeDriver{ testMakeLocalDiscoveryDriver, }}, {"rendezvous driver", []testMakeDriver{ testMakeRendezVousFactory(targetRdvp), }}, {"mocked+rendezvous driver", []testMakeDriver{ testMakeMockedDriverFactory(NewMockDriverServer()), testMakeRendezVousFactory(targetRdvp), }}, {"localdisc+mocked+rendezvous driver", []testMakeDriver{ testMakeLocalDiscoveryDriver, testMakeMockedDriverFactory(NewMockDriverServer()), testMakeRendezVousFactory(targetRdvp), }}, } for i, tc := range cases { topic := fmt.Sprintf("test_topic_%d", i) t.Run(tc.Name, func(t *testing.T) { testMultipleDriversSubscribe(t, ctx, mn, topic, tc.Makes...) }) } } func testMultipleDriversSubscribe(t *testing.T, ctx context.Context, mn mocknet.Mocknet, topic string, makers ...testMakeDriver) { ctx, cancel := context.WithCancel(ctx) defer cancel() logger, cleanup := testutil.Logger(t) defer cleanup() var err error var p1, p2 host.Host var s1, s2 *Service { p1 = genLocalPeer(t, mn) drivers := []IDriver{} for _, maker := range makers { drivers = append(drivers, maker(t, logger, p1)) } s1, err = NewService(p1, logger, drivers...) require.NoError(t, err) } { p2 = genLocalPeer(t, mn) drivers := []IDriver{} for _, maker := range makers { drivers = append(drivers, maker(t, logger, p2)) } s2, err = NewService(p2, logger, drivers...) require.NoError(t, err) } err = mn.LinkAll() require.NoError(t, err) err = mn.ConnectAllButSelf() require.NoError(t, err) // try a first lookup, should find nothing { out := s2.FindPeers(ctx, topic) peers := testPeersChanToSlice(t, out) require.Len(t, peers, 0, "no peer should be available") } // subscribe... sub := s2.Subscribe(topic) require.NoError(t, err) // ...then advertise err = s1.StartAdvertises(ctx, topic) require.NoError(t, err) { // should find exactly one peer p, err := testWaitForPeers(t, sub.Out(), time.Second*5) require.NoError(t, err) require.Equal(t, p1.ID(), p.ID) require.Equal(t, p1.Addrs(), p.Addrs) } { // should not have any peers left in the queue p, err := testWaitForPeers(t, sub.Out(), time.Millisecond*100) require.Nil(t, p) require.Error(t, err) } // try a lookup again, this time we should have some peers { out := s2.FindPeers(ctx, topic) p, err := testWaitForPeers(t, out, time.Second*5) require.NoError(t, err) require.Equal(t, p1.ID(), p.ID) require.Equal(t, p1.Addrs(), p.Addrs) // empty the channel, should not any peers left err = testDrainChannel(t, out, time.Second*5) require.NoError(t, err) } } func testMakeRendezVousFactory(target peer.ID) testMakeDriver { rng := rand.New(rand.NewSource(rand.Int63())) return func(t *testing.T, logger *zap.Logger, p host.Host) IDriver { t.Helper() syncClient := rendezvous.NewSyncInMemClient(context.Background(), p) return NewRendezvousDiscovery(logger, p, target, PrivateAddrsOnlyFactory, rng, syncClient) } } func testMakeMockedDriverFactory(srv *MockDriverServer) testMakeDriver { return func(t *testing.T, logger *zap.Logger, p host.Host) IDriver { return srv.Client(p) } } func testMakeDHTDriver(t *testing.T, logger *zap.Logger, p host.Host) IDriver { t.Helper() ctx, cancel := context.WithCancel(context.Background()) t.Cleanup(cancel) d, err := dht.New(ctx, p) require.NoError(t, err) t.Cleanup(func() { d.Close() }) return NewRoutingDiscoveryDriver("dht", d) } func testMakeLocalDiscoveryDriver(t *testing.T, logger *zap.Logger, p host.Host) IDriver { t.Helper() driver, err := NewLocalDiscovery(logger, p, rand.New(rand.NewSource(rand.Int63()))) require.NoError(t, err, "unable to make local discovery driver") return driver } ================================================ FILE: pkg/tinder/filter.go ================================================ package tinder import ( ma "github.com/multiformats/go-multiaddr" manet "github.com/multiformats/go-multiaddr/net" ) // keep public addr only func PublicAddrsOnlyFactory(ms []ma.Multiaddr) []ma.Multiaddr { return ma.FilterAddrs(ms, manet.IsPublicAddr) } // keep private addr only func PrivateAddrsOnlyFactory(ms []ma.Multiaddr) []ma.Multiaddr { return ma.FilterAddrs(ms, manet.IsPrivateAddr) } func AllAddrsFactory(ms []ma.Multiaddr) []ma.Multiaddr { return ms } ================================================ FILE: pkg/tinder/notify_network.go ================================================ package tinder import ( "context" "sync" "github.com/libp2p/go-libp2p/core/event" "github.com/libp2p/go-libp2p/core/host" bhost "github.com/libp2p/go-libp2p/p2p/host/basic" ma "github.com/multiformats/go-multiaddr" "go.uber.org/zap" "berty.tech/weshnet/v2/internal/notify" "berty.tech/weshnet/v2/pkg/logutil" ) type AddrsFilter = bhost.AddrsFactory type NetworkUpdate struct { logger *zap.Logger notify *notify.Notify locker *sync.Mutex sub event.Subscription once sync.Once currentAddrs []ma.Multiaddr } func NewNetworkUpdate(logger *zap.Logger, h host.Host) (*NetworkUpdate, error) { sub, err := h.EventBus().Subscribe(new(event.EvtLocalAddressesUpdated)) if err != nil { return nil, err } locker := &sync.Mutex{} nu := &NetworkUpdate{ logger: logger, sub: sub, locker: locker, notify: notify.New(locker), currentAddrs: h.Network().ListenAddresses(), } go nu.subscribeToNetworkUpdate() nu.logger.Debug("network update subscribe started") return nu, nil } func (n *NetworkUpdate) WaitForUpdate(ctx context.Context, currentAddrs []ma.Multiaddr) (diff []ma.Multiaddr, ok bool) { n.locker.Lock() defer n.locker.Unlock() for { // check for new/removed addrs if diff := diffAddrs(currentAddrs, n.currentAddrs); len(diff) > 0 { return diff, true } // wait until context is done or network is updated if ok := n.notify.Wait(ctx); !ok { return []ma.Multiaddr{}, false } } } func (n *NetworkUpdate) GetLastUpdatedAddrs(context.Context) (addrs []ma.Multiaddr) { n.locker.Lock() addrs = n.currentAddrs n.locker.Unlock() return } func (n *NetworkUpdate) subscribeToNetworkUpdate() { for evt := range n.sub.Out() { e := evt.(event.EvtLocalAddressesUpdated) if e.Diffs { // log diffs var nadd, ndel int for _, uaddr := range e.Current { switch uaddr.Action { case event.Added: n.logger.Debug("new addr", logutil.PrivateString("addr", uaddr.Address.String())) nadd++ case event.Removed: n.logger.Debug("removed addr", logutil.PrivateString("addr", uaddr.Address.String())) ndel++ } } n.logger.Debug("network update", zap.Int("del", ndel), zap.Int("add", nadd), zap.Int("total", nadd+ndel)) // update current addrs n.locker.Lock() n.currentAddrs = getAddrsFromUpdatedAddress(e.Current) n.notify.Broadcast() n.locker.Unlock() } } } func (n *NetworkUpdate) Close() (err error) { // use once to avoid panic if called twice n.once.Do(func() { err = n.sub.Close() }) return err } func diffAddrs(a, b []ma.Multiaddr) []ma.Multiaddr { diff := []ma.Multiaddr{} seta := make(map[string]ma.Multiaddr, len(a)) for _, addr := range a { seta[addr.String()] = addr } setb := make(map[string]struct{}) for _, maddr := range b { key := maddr.String() if _, found := seta[key]; !found { delete(seta, key) diff = append(diff, maddr) } else { setb[key] = struct{}{} } } for key, maddr := range seta { if _, found := setb[key]; !found { diff = append(diff, maddr) } } return diff } func getAddrsFromUpdatedAddress(updated []event.UpdatedAddress) []ma.Multiaddr { addrs := make([]ma.Multiaddr, len(updated)) for i, uaddr := range updated { addrs[i] = uaddr.Address } return addrs } ================================================ FILE: pkg/tinder/options.go ================================================ package tinder type Filter map[string]struct{} func (f Filter) ShouldFilter(name string) (yes bool) { if f == nil { return } _, yes = f[name] return } type Options struct { DriverFilters Filter } type Option func(opts *Options) error func (o *Options) apply(opts ...Option) error { for _, opt := range opts { if err := opt(o); err != nil { return err } } return nil } func FilterOutDrivers(drivers ...string) Option { return func(opts *Options) error { opts.DriverFilters = map[string]struct{}{} for _, driver := range drivers { opts.DriverFilters[driver] = struct{}{} } return nil } } ================================================ FILE: pkg/tinder/peer_cache.go ================================================ package tinder import ( "context" "sync" "time" "github.com/libp2p/go-libp2p/core/peer" ma "github.com/multiformats/go-multiaddr" "berty.tech/weshnet/v2/internal/notify" ) type PeersUpdate map[peer.ID]time.Time func (current PeersUpdate) HasUpdate(tu *topicUpdate) []peer.ID { peers := []peer.ID{} for p, update := range tu.peerUpdate { if lastUpdate, ok := current[p]; !ok || update.After(lastUpdate) { current[p] = update peers = append(peers, p) } } return peers } type topicUpdate struct { peerUpdate map[peer.ID]time.Time notify *notify.Notify } type peersCache struct { topics map[string]*topicUpdate muCache sync.RWMutex peers map[peer.ID]peer.AddrInfo muPeers sync.RWMutex } func newPeerCache() *peersCache { return &peersCache{ topics: make(map[string]*topicUpdate), peers: make(map[peer.ID]peer.AddrInfo), } } func (c *peersCache) UpdatePeer(topic string, p peer.AddrInfo) (isNew bool) { c.muPeers.Lock() defer c.muPeers.Unlock() tu := c.getTopicUpdate(topic) // update peer update time tu.notify.L.Lock() defer tu.notify.L.Unlock() _, exist := tu.peerUpdate[p.ID] // do we already know this peer ? if prev, ok := c.peers[p.ID]; ok { if combined := mergeAddrInfos(prev, p); combined != nil { c.peers[p.ID] = *combined } else if exist { // we already know this peer, and no change have been provided return isNew } } else { c.peers[p.ID] = p } t := time.Now() tu.peerUpdate[p.ID] = t // notify topic that peers has been updated tu.notify.Broadcast() return true } func (c *peersCache) GetPeers(ids ...peer.ID) (peers []peer.AddrInfo) { c.muPeers.RLock() peers = make([]peer.AddrInfo, len(ids)) for i, id := range ids { peers[i] = c.peers[id] } c.muPeers.RUnlock() return } func (c *peersCache) GetPeersForTopics(topic string) (peers []peer.AddrInfo) { c.muPeers.RLock() defer c.muPeers.RUnlock() tu := c.getTopicUpdate(topic) tu.notify.L.Lock() defer tu.notify.L.Unlock() i := 0 peers = make([]peer.AddrInfo, len(tu.peerUpdate)) for peer := range tu.peerUpdate { peers[i] = c.peers[peer] i++ } return } func (c *peersCache) WaitForPeerUpdate(ctx context.Context, topic string, current PeersUpdate) (updated []peer.ID, ok bool) { tu := c.getTopicUpdate(topic) tu.notify.L.Lock() for ok = true; ok; { // check if there are some diff between local state and the current state if updated = current.HasUpdate(tu); !ok || len(updated) > 0 { break // we got some update, leave the loop } // wait until there is an update on this group or context expire // unlock notify locker ok = tu.notify.Wait(ctx) } tu.notify.L.Unlock() return } func (c *peersCache) RemoveFromCache(_ context.Context, topic string, p peer.ID) (ok bool) { c.muCache.Lock() var tu *topicUpdate if tu, ok = c.topics[topic]; ok { if _, ok = tu.peerUpdate[p]; ok { delete(tu.peerUpdate, p) } } c.muCache.Unlock() return } func (c *peersCache) getTopicUpdate(topic string) *topicUpdate { c.muCache.Lock() tu, ok := c.topics[topic] if !ok { tu = &topicUpdate{ peerUpdate: make(map[peer.ID]time.Time), notify: notify.New(&sync.Mutex{}), } c.topics[topic] = tu } c.muCache.Unlock() return tu } func mergeAddrInfos(prevAi, newAi peer.AddrInfo) *peer.AddrInfo { combinedAddrs := uniqueAddrs(prevAi.Addrs, newAi.Addrs) if len(combinedAddrs) > len(prevAi.Addrs) { combinedAi := &peer.AddrInfo{ID: prevAi.ID, Addrs: combinedAddrs} return combinedAi } return nil } // uniqueAddrs merges input address lists, leave only unique addresses func uniqueAddrs(addrss ...[]ma.Multiaddr) (uniqueAddrs []ma.Multiaddr) { exists := make(map[string]bool) for _, addrs := range addrss { for _, addr := range addrs { k := string(addr.Bytes()) if exists[k] { continue } exists[k] = true uniqueAddrs = append(uniqueAddrs, addr) } } return uniqueAddrs } ================================================ FILE: pkg/tinder/peer_cache_test.go ================================================ package tinder import ( "context" "runtime" "testing" "time" "github.com/libp2p/go-libp2p/core/peer" ma "github.com/multiformats/go-multiaddr" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestPeerCacheUpdatePeer(t *testing.T) { const topic = "test_topic" if runtime.GOOS == "windows" { t.Skip("unittest not consistent on windows, skipping.") } peer := peer.AddrInfo{ ID: "hello", Addrs: []ma.Multiaddr{ ma.StringCast("/ip6/::/tcp/1234"), }, } pc := newPeerCache() t.Run("add new peer", func(t *testing.T) { pc.UpdatePeer(topic, peer) tu, ok := pc.topics[topic] require.True(t, ok) require.NotNil(t, tu) ti, ok := tu.peerUpdate[peer.ID] require.True(t, ok) assert.True(t, time.Now().After(ti)) speer, ok := pc.peers[peer.ID] require.True(t, ok) assert.Equal(t, peer, speer) }) t.Run("edit peer multiaddrs", func(t *testing.T) { peer.Addrs = []ma.Multiaddr{ma.StringCast("/ip4/1.2.3.4/tcp/1234")} pc.UpdatePeer(topic, peer) speer, ok := pc.peers[peer.ID] require.True(t, ok) assert.Len(t, speer.Addrs, 2) }) } func TestPeerCacheWaitForUpdate(t *testing.T) { const topic = "test_topic" pc := newPeerCache() pu := make(PeersUpdate) ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) out := make(chan []peer.ID) go func() { defer cancel() for { updated, ok := pc.WaitForPeerUpdate(ctx, topic, pu) if !ok { return } out <- updated } }() t.Run("should not update on start", func(t *testing.T) { select { case <-out: require.FailNow(t, "should not have received an update") case <-ctx.Done(): require.FailNow(t, "context should not be expired") case <-time.After(time.Millisecond * 100): } }) p1 := peer.AddrInfo{ ID: "peer1", Addrs: []ma.Multiaddr{ma.StringCast("/ip4/1.2.3.4/tcp/1234")}, } t.Run("should correctly update", func(t *testing.T) { pc.UpdatePeer(topic, p1) var updated []peer.ID select { case <-ctx.Done(): require.NoError(t, ctx.Err()) case updated = <-out: require.Len(t, updated, 1) } ps := pc.GetPeers(updated...) require.Len(t, ps, 1) assert.Equal(t, p1, ps[0]) }) t.Run("should not update on same peer", func(t *testing.T) { pc.UpdatePeer(topic, p1) select { case <-out: require.FailNow(t, "should not have received an update") case <-ctx.Done(): require.NoError(t, ctx.Err()) case <-time.After(time.Millisecond * 100): } }) p2 := peer.AddrInfo{ ID: "peer2", Addrs: []ma.Multiaddr{ma.StringCast("/ip6/::/tcp/1234")}, } t.Run("should update on new peer", func(t *testing.T) { pc.UpdatePeer(topic, p2) var updated []peer.ID select { case <-ctx.Done(): require.NoError(t, ctx.Err()) case updated = <-out: require.Len(t, updated, 1) } ps := pc.GetPeersForTopics(topic) require.Len(t, ps, 2) }) } ================================================ FILE: pkg/tinder/records.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.34.2 // protoc (unknown) // source: tinder/records.proto package tinder import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Records struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Records []*Record `protobuf:"bytes,1,rep,name=records,proto3" json:"records,omitempty"` } func (x *Records) Reset() { *x = Records{} if protoimpl.UnsafeEnabled { mi := &file_tinder_records_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Records) String() string { return protoimpl.X.MessageStringOf(x) } func (*Records) ProtoMessage() {} func (x *Records) ProtoReflect() protoreflect.Message { mi := &file_tinder_records_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Records.ProtoReflect.Descriptor instead. func (*Records) Descriptor() ([]byte, []int) { return file_tinder_records_proto_rawDescGZIP(), []int{0} } func (x *Records) GetRecords() []*Record { if x != nil { return x.Records } return nil } type Record struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Cid string `protobuf:"bytes,1,opt,name=cid,proto3" json:"cid,omitempty"` Expire int64 `protobuf:"varint,2,opt,name=expire,proto3" json:"expire,omitempty"` } func (x *Record) Reset() { *x = Record{} if protoimpl.UnsafeEnabled { mi := &file_tinder_records_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Record) String() string { return protoimpl.X.MessageStringOf(x) } func (*Record) ProtoMessage() {} func (x *Record) ProtoReflect() protoreflect.Message { mi := &file_tinder_records_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Record.ProtoReflect.Descriptor instead. func (*Record) Descriptor() ([]byte, []int) { return file_tinder_records_proto_rawDescGZIP(), []int{1} } func (x *Record) GetCid() string { if x != nil { return x.Cid } return "" } func (x *Record) GetExpire() int64 { if x != nil { return x.Expire } return 0 } var File_tinder_records_proto protoreflect.FileDescriptor var file_tinder_records_proto_rawDesc = []byte{ 0x0a, 0x14, 0x74, 0x69, 0x6e, 0x64, 0x65, 0x72, 0x2f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x74, 0x69, 0x6e, 0x64, 0x65, 0x72, 0x22, 0x33, 0x0a, 0x07, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x12, 0x28, 0x0a, 0x07, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x69, 0x6e, 0x64, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x07, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x22, 0x32, 0x0a, 0x06, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x63, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x63, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x42, 0x22, 0x5a, 0x20, 0x62, 0x65, 0x72, 0x74, 0x79, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x2f, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2f, 0x76, 0x32, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x69, 0x6e, 0x64, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( file_tinder_records_proto_rawDescOnce sync.Once file_tinder_records_proto_rawDescData = file_tinder_records_proto_rawDesc ) func file_tinder_records_proto_rawDescGZIP() []byte { file_tinder_records_proto_rawDescOnce.Do(func() { file_tinder_records_proto_rawDescData = protoimpl.X.CompressGZIP(file_tinder_records_proto_rawDescData) }) return file_tinder_records_proto_rawDescData } var file_tinder_records_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_tinder_records_proto_goTypes = []any{ (*Records)(nil), // 0: tinder.Records (*Record)(nil), // 1: tinder.Record } var file_tinder_records_proto_depIdxs = []int32{ 1, // 0: tinder.Records.records:type_name -> tinder.Record 1, // [1:1] is the sub-list for method output_type 1, // [1:1] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name } func init() { file_tinder_records_proto_init() } func file_tinder_records_proto_init() { if File_tinder_records_proto != nil { return } if !protoimpl.UnsafeEnabled { file_tinder_records_proto_msgTypes[0].Exporter = func(v any, i int) any { switch v := v.(*Records); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_tinder_records_proto_msgTypes[1].Exporter = func(v any, i int) any { switch v := v.(*Record); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_tinder_records_proto_rawDesc, NumEnums: 0, NumMessages: 2, NumExtensions: 0, NumServices: 0, }, GoTypes: file_tinder_records_proto_goTypes, DependencyIndexes: file_tinder_records_proto_depIdxs, MessageInfos: file_tinder_records_proto_msgTypes, }.Build() File_tinder_records_proto = out.File file_tinder_records_proto_rawDesc = nil file_tinder_records_proto_goTypes = nil file_tinder_records_proto_depIdxs = nil } ================================================ FILE: pkg/tinder/service.go ================================================ package tinder import ( "context" "fmt" "sync" "sync/atomic" "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/peer" "go.uber.org/zap" "berty.tech/weshnet/v2/pkg/logutil" ) type Service struct { host host.Host logger *zap.Logger drivers []IDriver networkNotify *NetworkUpdate topicCounter map[string]*Subscription muTopics sync.Mutex // subscribe peersCache *peersCache process uint32 } func NewService(h host.Host, logger *zap.Logger, drivers ...IDriver) (*Service, error) { nn, err := NewNetworkUpdate(logger, h) if err != nil { return nil, fmt.Errorf("unable to init service: %w", err) } return &Service{ host: h, logger: logger.Named("tinder"), drivers: drivers, networkNotify: nn, topicCounter: make(map[string]*Subscription), peersCache: newPeerCache(), }, nil } func (s *Service) FindPeers(ctx context.Context, topic string) <-chan peer.AddrInfo { s.muTopics.Lock() defer s.muTopics.Unlock() ctx, cancel := context.WithCancel(ctx) go func() { if err := s.LookupPeers(ctx, topic); err != nil { s.logger.Error("lookup failed", logutil.PrivateString("topic", topic), zap.Error(err)) } cancel() }() return s.fadeOut(ctx, topic, 16) } // Unregister try to unregister topic on each of his driver func (s *Service) Unregister(ctx context.Context, topic string) error { var wg sync.WaitGroup var success int32 for _, driver := range s.drivers { wg.Add(1) go func(driver IDriver) { if err := driver.Unregister(ctx, topic); err != nil { s.logger.Debug("unable to advertise", zap.Error(err)) } else { atomic.AddInt32(&success, 1) } wg.Done() }(driver) } wg.Wait() if success == 0 { return fmt.Errorf("no driver(s) were available for unregister") } return nil } func (s *Service) WatchPeers(ctx context.Context, topic string) <-chan peer.AddrInfo { return s.fadeOut(ctx, topic, 16) } func (s *Service) fadeIn(ctx context.Context, topic string, in <-chan peer.AddrInfo) { s.incProcess() defer s.decProcess() for { select { case p, ok := <-in: if !ok { return } if updated := s.peersCache.UpdatePeer(topic, p); updated { s.logger.Debug("topic updated", zap.String("topic", topic), zap.String("peer", p.String())) } case <-ctx.Done(): return } } } func (s *Service) fadeOut(ctx context.Context, topic string, bufsize int) <-chan peer.AddrInfo { out := make(chan peer.AddrInfo, bufsize) go func() { knownPeers := make(PeersUpdate) for { // Wait until `PeersCache` differ from `peerCache` peers status updated, ok := s.peersCache.WaitForPeerUpdate(ctx, topic, knownPeers) if !ok { break } s.logger.Debug("got update on peer", zap.String("topic", topic), zap.Int("peers", len(updated))) for _, peer := range s.peersCache.GetPeers(updated...) { out <- peer } } // we're done here, close the channel and decrement process close(out) }() return out } func (s *Service) Close() error { return s.networkNotify.Close() } func (s *Service) GetProcess() uint32 { return atomic.LoadUint32(&s.process) } func (s *Service) incProcess() { atomic.AddUint32(&s.process, 1) } func (s *Service) decProcess() { atomic.AddUint32(&s.process, ^(uint32)(0)) } ================================================ FILE: pkg/tinder/service_adaptater.go ================================================ package tinder import ( "context" "sync" "time" "github.com/libp2p/go-libp2p/core/discovery" "github.com/libp2p/go-libp2p/core/peer" "go.uber.org/zap" "berty.tech/weshnet/v2/pkg/logutil" ) var _ discovery.Discovery = (*DiscoveryAdaptater)(nil) type discoverySubscribtion struct { sub *Subscription timer *time.Timer } type DiscoveryAdaptater struct { ctx context.Context cancel context.CancelFunc logger *zap.Logger defaultOpts []Option service *Service // watchdogDiscover watchdogDiscover map[string]*discoverySubscribtion muDiscover sync.Mutex // advertise watchdogAdvertise map[string]*time.Timer muAdvertiser sync.Mutex resetInterval time.Duration ttl time.Duration closeOnce sync.Once } func NewDiscoveryAdaptater(logger *zap.Logger, service *Service, defaultOpts ...Option) *DiscoveryAdaptater { ctx, cancel := context.WithCancel(context.Background()) return &DiscoveryAdaptater{ ctx: ctx, cancel: cancel, logger: logger.Named("disc"), watchdogDiscover: make(map[string]*discoverySubscribtion), watchdogAdvertise: make(map[string]*time.Timer), service: service, resetInterval: time.Minute * 10, ttl: time.Minute * 5, defaultOpts: defaultOpts, } } func (a *DiscoveryAdaptater) FindPeers(_ context.Context, topic string, _ ...discovery.Option) (<-chan peer.AddrInfo, error) { a.muDiscover.Lock() defer a.muDiscover.Unlock() if st, ok := a.watchdogDiscover[topic]; ok { // already running FindPeers, reset watchdog if !st.timer.Stop() { <-st.timer.C } st.timer.Reset(a.resetInterval) // watch again for new peers until the method expire return st.sub.Out(), nil } start := time.Now() a.logger.Debug("watchdogs looking for peers", logutil.PrivateString("topic", topic)) // filter out local discovery sub := a.service.Subscribe(topic, a.defaultOpts...) // pull to fetch previous peers (FindPeers) go func() { if err := sub.Pull(); err != nil { a.logger.Error("unable to pull topic", zap.String("topic", topic), zap.Error(err)) } }() // create a new watchdog timer := time.AfterFunc(a.resetInterval, func() { a.logger.Debug("findpeers expired", logutil.PrivateString("topic", topic), zap.Duration("duration", time.Since(start))) // watchdog has expired, cancel lookup+topic_watcher a.muDiscover.Lock() sub.Close() delete(a.watchdogDiscover, topic) a.muDiscover.Unlock() }) a.watchdogDiscover[topic] = &discoverySubscribtion{ timer: timer, sub: sub, } // watch for new peers until method context expire return sub.Out(), nil } func (a *DiscoveryAdaptater) Advertise(_ context.Context, topic string, _ ...discovery.Option) (time.Duration, error) { ctx := a.ctx a.muAdvertiser.Lock() defer a.muAdvertiser.Unlock() start := time.Now() if t, ok := a.watchdogAdvertise[topic]; ok { // if we already advertise on this topic, reset the watchdog if !t.Stop() { <-t.C } t.Reset(a.resetInterval) } else { wctx, cancel := context.WithCancel(ctx) // start advertising on this topic // filter out local discovery if err := a.service.StartAdvertises(wctx, topic, a.defaultOpts...); err != nil { a.logger.Error("advertise failed", logutil.PrivateString("topic", topic), zap.Error(err)) cancel() return time.Minute, err } a.logger.Debug("advertise started", logutil.PrivateString("topic", topic)) // create a new watchdog a.watchdogAdvertise[topic] = time.AfterFunc(a.resetInterval, func() { // watchdog has expired, cancel advertise a.muAdvertiser.Lock() cancel() a.logger.Debug("advertise expired", logutil.PrivateString("topic", topic), zap.Duration("duration", time.Since(start)), ) delete(a.watchdogAdvertise, topic) a.muAdvertiser.Unlock() // unregister from this topic if possible if err := a.service.Unregister(ctx, topic); err != nil { a.logger.Debug("unregister failed", logutil.PrivateString("topic", topic), zap.Error(err), ) } }) } return a.ttl, nil } func (a *DiscoveryAdaptater) Close() error { a.closeOnce.Do(func() { a.muDiscover.Lock() a.cancel() for _, st := range a.watchdogDiscover { if !st.timer.Stop() { <-st.timer.C } _ = st.sub.Close() } a.muDiscover.Unlock() a.muAdvertiser.Lock() for _, t := range a.watchdogAdvertise { if !t.Stop() { <-t.C } } a.muAdvertiser.Unlock() }) return nil } ================================================ FILE: pkg/tinder/service_advertises.go ================================================ package tinder import ( "context" "fmt" "time" "go.uber.org/zap" "berty.tech/weshnet/v2/pkg/logutil" ) const defaultTTL = time.Hour // StartAdvertises topic on each of service drivers func (s *Service) StartAdvertises(ctx context.Context, topic string, opts ...Option) error { if len(s.drivers) == 0 { return fmt.Errorf("no driver available to advertise") } var aopts Options if err := aopts.apply(opts...); err != nil { return fmt.Errorf("failed to advertise: %w", err) } for _, driver := range s.drivers { // skip filter driver if aopts.DriverFilters.ShouldFilter(driver.Name()) { continue } // start background job go func(driver IDriver) { if err := s.advertise(ctx, driver, topic); err != nil { s.logger.Debug("advertise ended", zap.Error(err)) } }(driver) } return nil } func (s *Service) advertise(ctx context.Context, d IDriver, topic string) error { for { currentAddrs := s.networkNotify.GetLastUpdatedAddrs(ctx) now := time.Now() ttl, err := d.Advertise(ctx, topic) took := time.Since(now) var deadline time.Duration if err != nil { select { case <-ctx.Done(): return ctx.Err() default: } // retry in 30 seconds deadline = time.Second * 30 } else { if ttl == 0 { ttl = defaultTTL } deadline = 4 * ttl / 5 } s.logger.Debug("advertise", zap.String("driver", d.Name()), logutil.PrivateString("topic", topic), zap.Duration("ttl", ttl), zap.Duration("took", took), zap.Duration("next", deadline), zap.Error(err), ) waitctx, cancel := context.WithTimeout(ctx, deadline) // wait for network update or waitctx expire _, ok := s.networkNotify.WaitForUpdate(waitctx, currentAddrs) cancel() // filter addrs if !ok { select { // check for parent ctx case <-ctx.Done(): // main context has expire, stop return ctx.Err() default: // waitContext has expire, continue } } } } ================================================ FILE: pkg/tinder/service_mocked_test.go ================================================ package tinder import ( "context" "fmt" "testing" "time" "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/peer" mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap" "berty.tech/weshnet/v2/pkg/testutil" ) func TestMockedServiceSubscribeMultipleDriver(t *testing.T) { const nPeers = 100 const topic = "test_topic" ctx, cancel := context.WithCancel(context.Background()) defer cancel() mn := mocknet.New() defer mn.Close() cases := []struct { NDriver, NPeersPerDriver int }{ {1, 1}, {1, 10}, {1, 100}, {100, 1}, {10, 10}, } for _, tc := range cases { name := fmt.Sprintf("%d_dirvers-%d_peer_per_driver", tc.NDriver, tc.NPeersPerDriver) t.Run(name, func(t *testing.T) { rootp, err := mn.GenPeer() require.NoError(t, err) clients := make([]IDriver, tc.NDriver*tc.NPeersPerDriver) drivers := make([]IDriver, tc.NDriver) for i := 0; i < tc.NDriver; i++ { srv := NewMockDriverServer() drivers[i] = srv.Client(rootp) for j := 0; j < tc.NPeersPerDriver; j++ { p, err := mn.GenPeer() require.NoError(t, err) index := tc.NPeersPerDriver*i + j clients[index] = srv.Client(p) } } service, err := NewService(rootp, zap.NewNop(), drivers...) require.NoError(t, err) for _, client := range clients { _, err := client.Advertise(ctx, topic) assert.NoError(t, err) } sub := service.Subscribe(topic) defer sub.Close() err = sub.Pull() require.NoError(t, err) seen := make(map[peer.ID]struct{}) var count int for count = 0; count < len(clients); count++ { select { case p := <-sub.Out(): if _, ok := seen[p.ID]; ok { require.FailNow(t, "should only receive a peer once, received") } seen[p.ID] = struct{}{} case <-time.After(time.Second): require.FailNow(t, "timeout while waiting for peer", "received: %d", count) } } assert.Equal(t, tc.NDriver*tc.NPeersPerDriver, count) }) } } func TestMockedServiceSubscribePull(t *testing.T) { const topic = "test_topic" ctx, cancel := context.WithCancel(context.Background()) defer cancel() logger, cleanup := testutil.Logger(t) defer cleanup() mn := mocknet.New() defer mn.Close() srv := NewMockDriverServer() t.Run("with pull", func(t *testing.T) { const topic = "test_topic_1" p1, s1 := newTestMockedService(t, logger, mn, srv) _, s2 := newTestMockedService(t, logger, mn, srv) err := s1.StartAdvertises(ctx, topic) require.NoError(t, err) err = srv.WaitForPeer(topic, p1.ID(), time.Second) require.NoError(t, err) sub := s2.Subscribe(topic) defer sub.Close() err = sub.Pull() require.NoError(t, err) select { case p := <-sub.Out(): assert.Equal(t, p1.ID(), p.ID) assert.Equal(t, p1.Addrs(), p.Addrs) case <-time.After(time.Second * 2): require.FailNow(t, "timeout while waiting for peer") } }) t.Run("no pull", func(t *testing.T) { const topic = "test_topic_2" p1, s1 := newTestMockedService(t, logger, mn, srv) _, s2 := newTestMockedService(t, logger, mn, srv) err := s1.StartAdvertises(ctx, topic) require.NoError(t, err) err = srv.WaitForPeer(topic, p1.ID(), time.Second) require.NoError(t, err) sub := s2.Subscribe(topic) defer sub.Close() select { case <-sub.Out(): require.FailNow(t, "do no expect peers") case <-time.After(time.Millisecond * 500): } }) } func TestMockedServiceSubscribeDuplicatePeer(t *testing.T) { const topic = "test_topic" const NServers = 10 ctx, cancel := context.WithCancel(context.Background()) defer cancel() logger, cleanup := testutil.Logger(t) defer cleanup() mn := mocknet.New() defer mn.Close() servers := make([]*MockDriverServer, NServers) for i := range servers { servers[i] = NewMockDriverServer() } p1, s1 := newTestMockedService(t, logger, mn, servers...) _, s2 := newTestMockedService(t, logger, mn, servers...) err := s1.StartAdvertises(ctx, topic) require.NoError(t, err) for _, s := range servers { err := s.WaitForPeer(topic, p1.ID(), time.Second) require.NoError(t, err) } sub := s2.Subscribe(topic) defer sub.Close() err = sub.Pull() require.NoError(t, err) var count int for { select { case p := <-sub.Out(): assert.Equal(t, p1.ID(), p.ID) assert.Equal(t, p1.Addrs(), p.Addrs) count++ case <-time.After(time.Millisecond * 500): require.Equal(t, 1, count) return } } } func newTestMockedService(t *testing.T, logger *zap.Logger, mn mocknet.Mocknet, srvs ...*MockDriverServer) (host.Host, *Service) { t.Helper() h, err := mn.GenPeer() require.NoError(t, err) drivers := make([]IDriver, len(srvs)) for i, srv := range srvs { drivers[i] = srv.Client(h) } s, err := NewService(h, logger, drivers...) require.NoError(t, err) return h, s } ================================================ FILE: pkg/tinder/service_subscription.go ================================================ package tinder import ( "context" "fmt" "github.com/libp2p/go-libp2p/core/peer" "go.uber.org/zap" ) type Subscription struct { cancel context.CancelFunc ctx context.Context service *Service topic string out <-chan peer.AddrInfo opts []Option } func (s *Subscription) Out() <-chan peer.AddrInfo { return s.out } func (s *Subscription) Pull() error { return s.service.LookupPeers(s.ctx, s.topic, s.opts...) } func (s *Subscription) Close() error { s.cancel() return nil } func (s *Service) Subscribe(topic string, opts ...Option) *Subscription { ctx, cancel := context.WithCancel(context.Background()) out := s.fadeOut(ctx, topic, 16) err := s.WatchTopic(ctx, topic, opts...) if err != nil { s.logger.Warn("unable to watch topic", zap.String("topic", topic), zap.Error(err)) } return &Subscription{ service: s, out: out, ctx: ctx, cancel: cancel, topic: topic, opts: opts, } } func (s *Service) LookupPeers(ctx context.Context, topic string, opts ...Option) error { var success int var aopts Options if err := aopts.apply(opts...); err != nil { return fmt.Errorf("unable to apply option: %w", err) } for _, d := range s.drivers { if aopts.DriverFilters.ShouldFilter(d.Name()) { continue } in, err := d.FindPeers(ctx, topic) // find peers should not hang there switch err { case nil: // ok success++ s.logger.Debug("lookup for topic started", zap.String("driver", d.Name()), zap.String("topic", topic)) go s.fadeIn(ctx, topic, in) case ErrNotSupported: // do nothing default: s.logger.Error("lookup failed", zap.String("driver", d.Name()), zap.String("topic", topic), zap.Error(err)) } } if success == 0 { return fmt.Errorf("no driver(s) were available for lookup") } return nil } func (s *Service) WatchTopic(ctx context.Context, topic string, opts ...Option) (err error) { var success int var aopts Options if err := aopts.apply(opts...); err != nil { return fmt.Errorf("unable to apply option: %w", err) } for _, d := range s.drivers { if aopts.DriverFilters.ShouldFilter(d.Name()) { continue } s.logger.Debug("start subscribe", zap.String("driver", d.Name()), zap.String("topic", topic)) in, err := d.Subscribe(ctx, topic) switch err { case nil: // ok, start fadin success++ s.logger.Debug("watching for topic update", zap.String("driver", d.Name()), zap.String("topic", topic)) go s.fadeIn(ctx, topic, in) case ErrNotSupported: // not, supported skipping default: s.logger.Error("unable to subscribe on topic", zap.String("driver", d.Name()), zap.String("topic", topic), zap.Error(err)) } } if success == 0 { err = fmt.Errorf("no driver(s) were available for subscribe") } return err } ================================================ FILE: pkg/tinder/testing_test.go ================================================ package tinder import ( "context" "fmt" "testing" "time" rendezvous "github.com/berty/go-libp2p-rendezvous" dbrdvp "github.com/berty/go-libp2p-rendezvous/db/sqlite" p2putil "github.com/libp2p/go-libp2p-testing/netutil" "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/peer" mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" ma "github.com/multiformats/go-multiaddr" "github.com/stretchr/testify/require" ) var ( ErrChannelNotEmpty = fmt.Errorf("channel not empty") ErrChannelTimeout = fmt.Errorf("waiting for channel: timeout") ) func makeRendezvousService(t *testing.T, mn mocknet.Mocknet) (target peer.ID, svc *rendezvous.RendezvousService) { ctx, cancel := context.WithCancel(context.Background()) t.Cleanup(cancel) peer, err := mn.GenPeer() require.NoError(t, err) pubsubsync, err := rendezvous.NewSyncInMemProvider(peer) require.NoError(t, err) dbi, err := dbrdvp.OpenDB(ctx, ":memory:") require.NoError(t, err) t.Cleanup(func() { _ = dbi.Close() }) return peer.ID(), rendezvous.NewRendezvousService(peer, dbi, pubsubsync) } func testWaitForPeers(t *testing.T, out <-chan peer.AddrInfo, timeout time.Duration) (*peer.AddrInfo, error) { t.Helper() select { case p := <-out: return &p, nil case <-time.After(timeout): return nil, fmt.Errorf("timeout while waiting for peer") } } func testDrainChannel(t *testing.T, out <-chan peer.AddrInfo, timeout time.Duration) error { t.Helper() for { select { case _, ok := <-out: if !ok { return nil // ok } return fmt.Errorf("channel wasn't empty") case <-time.After(timeout): return fmt.Errorf("timeout while waiting for peer") } } } func testPeersChanToSlice(t *testing.T, out <-chan peer.AddrInfo) (peers []*peer.AddrInfo) { t.Helper() peers = []*peer.AddrInfo{} for p := range out { peers = append(peers, &p) } return } func genLocalPeer(t *testing.T, mn mocknet.Mocknet) host.Host { sk, err := p2putil.RandTestBogusPrivateKey() require.NoError(t, err) a, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/0") require.NoError(t, err) h, err := mn.AddPeer(sk, a) require.NoError(t, err) return h } ================================================ FILE: pkg/tyber/context.go ================================================ package tyber import ( "context" "strconv" "time" "github.com/gofrs/uuid" "google.golang.org/grpc/metadata" ) type traceIDKeyType string const ( traceIDKey traceIDKeyType = "TyberTraceID" noTraceID string = "no_trace_id" uuidFallback string = "409123fa-4dd5-11eb-a4a1-675173c25b22" ) var ( NewSessionID = newID NewTraceID = newID ) func ContextWithTraceID(ctx context.Context) (context.Context, bool) { if eid := GetTraceIDFromContext(ctx); eid != noTraceID { return ctx, false } if md, ok := metadata.FromIncomingContext(ctx); ok { if vals := md.Get(string(traceIDKey)); len(vals) != 0 { return ContextWithConstantTraceID(ctx, vals[0]), false } } return ContextWithConstantTraceID(ctx, NewTraceID()), true } func ContextWithConstantTraceID(ctx context.Context, traceID string) context.Context { md, ok := metadata.FromOutgoingContext(ctx) if ok { md = md.Copy() } else { md = metadata.New(nil) } md.Set(string(traceIDKey), traceID) return metadata.NewOutgoingContext(context.WithValue(ctx, traceIDKey, traceID), md) } func GetTraceIDFromContext(ctx context.Context) string { if val, ok := ctx.Value(traceIDKey).(string); ok { return val } return noTraceID } func ContextWithoutTraceID(ctx context.Context) context.Context { return ContextWithConstantTraceID(ctx, noTraceID) } func newID() string { uid, err := uuid.NewV4() // If error while reading random, fallback on uuid v5 if err != nil { ns, err := uuid.FromString(uuidFallback) if err != nil { panic(err) // Should never happen } n := strconv.FormatInt(time.Now().UnixNano(), 10) uid = uuid.NewV5(ns, n) } return uid.String() } ================================================ FILE: pkg/tyber/format.go ================================================ package tyber import ( "context" "encoding/json" "fmt" "slices" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) type Detail struct { Name string `json:"name"` Description string `json:"description"` } func safeJSONMarshal(v any) string { bs, err := json.MarshalIndent(v, "", " ") if err != nil { return fmt.Sprintf(`"%s"`, err.Error()) } return string(bs) } func JSONDetail(name string, val any) Detail { return Detail{Name: name, Description: safeJSONMarshal(val)} } func WithJSONDetail(name string, val any) StepMutator { return func(s Step) Step { s.Details = append(s.Details, JSONDetail(name, val)) return s } } func WithError(err error) StepMutator { return func(s Step) Step { s.Details = append(s.Details, Detail{Name: "Error", Description: err.Error()}) return s } } type LogType string const ( TraceType LogType = "trace" StepType LogType = "step" EventType LogType = "event" SubscribeType LogType = "subcribe" ) var KnownLogTypes = []LogType{TraceType, StepType, EventType, SubscribeType} func (lt LogType) IsKnown() bool { return slices.Contains(KnownLogTypes, lt) } type StatusType string const ( Running StatusType = "running" Succeeded StatusType = "succeeded" Failed StatusType = "failed" ) type Trace struct { TraceID string `json:"traceID"` } type Event struct { Details []Detail `json:"details"` } func FormatTraceLogFields(ctx context.Context) []zapcore.Field { return []zapcore.Field{ zap.String("tyberLogType", string(TraceType)), zap.Any("trace", Trace{ TraceID: GetTraceIDFromContext(ctx), }), } } func FormatEventLogFields(_ context.Context, details []Detail) []zapcore.Field { return []zapcore.Field{ zap.String("tyberLogType", string(EventType)), zap.Any("event", Event{ Details: details, }), } } func ZapFieldsToDetails(fields ...zap.Field) []Detail { dets := make([]Detail, len(fields)) for i, field := range fields { dets[i] = Detail{Name: field.Key, Description: field.String} } return dets } ================================================ FILE: pkg/tyber/ipfs.go ================================================ package tyber import ( ipfscid "github.com/ipfs/go-cid" ) func WithCIDDetail(name string, cidBytes []byte) StepMutator { cid, err := ipfscid.Cast(cidBytes) if err != nil { return func(s Step) Step { return s } } return func(s Step) Step { s.Details = append(s.Details, Detail{Name: name, Description: cid.String()}) return s } } ================================================ FILE: pkg/tyber/log.go ================================================ package tyber import ( "context" "go.uber.org/zap" ) const tyberLogNS = "tyber" func LogError(ctx context.Context, logger *zap.Logger, text string, err error, mutators ...StepMutator) error { if logger == nil { return err } logger.Named(tyberLogNS).Error( text, FormatStepLogFields(ctx, []Detail{{Name: "Error", Description: err.Error()}}, mutators...)..., ) // returning the input error for better usage syntax return err } func LogFatalError(ctx context.Context, logger *zap.Logger, text string, err error, mutators ...StepMutator) error { return LogError(ctx, logger, text, err, append(mutators, Fatal)...) } func LogTraceEnd(ctx context.Context, logger *zap.Logger, text string, mutators ...StepMutator) { logger.Named(tyberLogNS).Debug(text, FormatStepLogFields(ctx, []Detail{}, append(mutators, EndTrace)...)...) } func LogTraceStart(ctx context.Context, logger *zap.Logger, text string) { logger.Named(tyberLogNS).Debug(text, FormatTraceLogFields(ctx)...) } func LogStep(ctx context.Context, logger *zap.Logger, text string, mutators ...StepMutator) { logger.Named(tyberLogNS).Debug(text, FormatStepLogFields(ctx, []Detail{}, mutators...)...) } ================================================ FILE: pkg/tyber/section.go ================================================ package tyber import ( "context" "go.uber.org/zap" ) func Section(ctx context.Context, logger *zap.Logger, name string) (context.Context, bool, func(error, string, ...StepMutator)) { ctx, newTrace := ContextWithTraceID(ctx) if newTrace { LogTraceStart(ctx, logger, name) } else { LogStep(ctx, logger, name) } return ctx, newTrace, func(err error, rename string, muts ...StepMutator) { if err != nil { errorName := "Error while " + name if rename != "" { errorName = rename } if newTrace { _ = LogFatalError(ctx, logger, errorName, err, muts...) } else { _ = LogError(ctx, logger, errorName, err, muts...) } } else { successName := name + " succeeded" if rename != "" { successName = rename } if newTrace { LogTraceEnd(ctx, logger, successName, muts...) } else { LogStep(ctx, logger, successName, muts...) } } } } func SimpleSection(ctx context.Context, logger *zap.Logger, name string) func(error, ...StepMutator) { _, _, end := Section(ctx, logger, name) return func(err error, muts ...StepMutator) { end(err, "", muts...) } } ================================================ FILE: pkg/tyber/step.go ================================================ package tyber import ( "context" "runtime/debug" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) // types type Step struct { ParentTraceID string `json:"parentTraceID"` Details []Detail `json:"details"` Status StatusType `json:"status"` EndTrace bool `json:"endTrace"` UpdateTraceName string `json:"updateTraceName"` ForceReopen bool `json:"forceReopen"` } type StepMutator = func(Step) Step // zap format func FormatStepLogFields(ctx context.Context, details []Detail, mutators ...StepMutator) []zapcore.Field { s := Step{ ParentTraceID: GetTraceIDFromContext(ctx), Status: Succeeded, Details: details, EndTrace: false, ForceReopen: false, } for _, m := range mutators { s = m(s) } // Add debug if a there is no parent trace ID if s.ParentTraceID == noTraceID { s.Details = append(s.Details, Detail{Name: "StackTrace", Description: string(debug.Stack())}) } return []zapcore.Field{ zap.String("tyberLogType", string(StepType)), zap.Any("step", s), } } // constant mutators func ForceReopen(s Step) Step { s.ForceReopen = true return s } func EndTrace(s Step) Step { s.EndTrace = true return s } func Fatal(s Step) Step { s.EndTrace = true s.Status = Failed return s } // variable mutators func Status(st StatusType) StepMutator { return func(s Step) Step { s.Status = st return s } } func UpdateTraceName(newTitle string) StepMutator { return func(s Step) Step { s.UpdateTraceName = newTitle return s } } func WithDetail(name string, description string) StepMutator { return func(s Step) Step { s.Details = append(s.Details, Detail{Name: name, Description: description}) return s } } ================================================ FILE: pkg/tyber/subscribe.go ================================================ package tyber import ( "context" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) type Subscribe struct { TargetStepName string `json:"targetStepName"` TargetDetails []Detail `json:"targetDetails"` StepToAdd Step `json:"stepToAdd"` } func FormatSubscribeLogFields(ctx context.Context, targetName string, targetDetails []Detail, stepToAddMutators ...StepMutator) []zapcore.Field { sta := Step{ ParentTraceID: GetTraceIDFromContext(ctx), Status: Succeeded, } for _, m := range stepToAddMutators { sta = m(sta) } return []zapcore.Field{ zap.String("tyberLogType", string(SubscribeType)), zap.Any("subscribe", Subscribe{ TargetStepName: targetName, TargetDetails: targetDetails, StepToAdd: sta, }), } } ================================================ FILE: pkg/username/android.go ================================================ //go:build android package username /* #include #include #include // Could be improved using Android Java API // https://medium.com/capital-one-tech/how-to-get-an-android-device-nickname-d5eab12f4ced const char* GetDeviceName() { char model[PROP_VALUE_MAX + 1]; int len = __system_property_get("ro.product.model", model); char *name = malloc(len + 1); memcpy(name, model, len); name[len] = 0; return name; } */ import "C" import "unsafe" const defaultUsername = "android#1337" func getUsername() string { cstring := C.GetDeviceName() username := C.GoString(cstring) C.free(unsafe.Pointer(cstring)) return username } ================================================ FILE: pkg/username/example_test.go ================================================ package username_test ================================================ FILE: pkg/username/ios.go ================================================ //go:build ios && !catalyst package username /* #cgo CFLAGS: -x objective-c #cgo darwin LDFLAGS: -framework Foundation -framework UIKit #import #import const char* GetDeviceName() { NSString *deviceName = [[UIDevice currentDevice] name]; char *copy = strdup([deviceName UTF8String]); return copy; } */ import "C" import "unsafe" const defaultUsername = "ios#1337" func getUsername() string { cstring := C.GetDeviceName() username := C.GoString(cstring) C.free(unsafe.Pointer(cstring)) return username } ================================================ FILE: pkg/username/others.go ================================================ //go:build (!android && !ios) || (ios && catalyst) package username import "os/user" const defaultUsername = "anon#1337" func getUsername() string { user, err := user.Current() if err != nil || user == nil { return "" } return user.Name } ================================================ FILE: pkg/username/username.go ================================================ package username import "strings" func GetUsername() string { username := getUsername() trimed := strings.TrimSpace(username) if trimed != "" { return trimed } return defaultUsername } ================================================ FILE: pkg/verifiablecredstypes/bertyverifiablecreds.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.34.2 // protoc (unknown) // source: verifiablecredstypes/bertyverifiablecreds.proto package verifiablecredstypes import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type FlowType int32 const ( FlowType_FlowTypeUndefined FlowType = 0 // FlowTypeCode asks users a code sent on a side channel FlowType_FlowTypeCode FlowType = 1 // FlowTypeAuth currently unimplemented FlowType_FlowTypeAuth FlowType = 2 // FlowTypeProof currently unimplemented FlowType_FlowTypeProof FlowType = 3 ) // Enum value maps for FlowType. var ( FlowType_name = map[int32]string{ 0: "FlowTypeUndefined", 1: "FlowTypeCode", 2: "FlowTypeAuth", 3: "FlowTypeProof", } FlowType_value = map[string]int32{ "FlowTypeUndefined": 0, "FlowTypeCode": 1, "FlowTypeAuth": 2, "FlowTypeProof": 3, } ) func (x FlowType) Enum() *FlowType { p := new(FlowType) *p = x return p } func (x FlowType) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (FlowType) Descriptor() protoreflect.EnumDescriptor { return file_verifiablecredstypes_bertyverifiablecreds_proto_enumTypes[0].Descriptor() } func (FlowType) Type() protoreflect.EnumType { return &file_verifiablecredstypes_bertyverifiablecreds_proto_enumTypes[0] } func (x FlowType) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use FlowType.Descriptor instead. func (FlowType) EnumDescriptor() ([]byte, []int) { return file_verifiablecredstypes_bertyverifiablecreds_proto_rawDescGZIP(), []int{0} } type CodeStrategy int32 const ( CodeStrategy_CodeStrategyUndefined CodeStrategy = 0 // CodeStrategy6Digits currently unimplemented CodeStrategy_CodeStrategy6Digits CodeStrategy = 1 // CodeStrategy10Chars currently unimplemented CodeStrategy_CodeStrategy10Chars CodeStrategy = 2 // CodeStrategyMocked6Zeroes must only be used in testing CodeStrategy_CodeStrategyMocked6Zeroes CodeStrategy = 999 ) // Enum value maps for CodeStrategy. var ( CodeStrategy_name = map[int32]string{ 0: "CodeStrategyUndefined", 1: "CodeStrategy6Digits", 2: "CodeStrategy10Chars", 999: "CodeStrategyMocked6Zeroes", } CodeStrategy_value = map[string]int32{ "CodeStrategyUndefined": 0, "CodeStrategy6Digits": 1, "CodeStrategy10Chars": 2, "CodeStrategyMocked6Zeroes": 999, } ) func (x CodeStrategy) Enum() *CodeStrategy { p := new(CodeStrategy) *p = x return p } func (x CodeStrategy) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (CodeStrategy) Descriptor() protoreflect.EnumDescriptor { return file_verifiablecredstypes_bertyverifiablecreds_proto_enumTypes[1].Descriptor() } func (CodeStrategy) Type() protoreflect.EnumType { return &file_verifiablecredstypes_bertyverifiablecreds_proto_enumTypes[1] } func (x CodeStrategy) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use CodeStrategy.Descriptor instead. func (CodeStrategy) EnumDescriptor() ([]byte, []int) { return file_verifiablecredstypes_bertyverifiablecreds_proto_rawDescGZIP(), []int{1} } // StateChallenge serialized and signed state used when requesting a challenge type StateChallenge struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Timestamp []byte `protobuf:"bytes,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` Nonce []byte `protobuf:"bytes,2,opt,name=nonce,proto3" json:"nonce,omitempty"` BertyLink string `protobuf:"bytes,3,opt,name=berty_link,json=bertyLink,proto3" json:"berty_link,omitempty"` RedirectUri string `protobuf:"bytes,4,opt,name=redirect_uri,json=redirectUri,proto3" json:"redirect_uri,omitempty"` State string `protobuf:"bytes,5,opt,name=state,proto3" json:"state,omitempty"` } func (x *StateChallenge) Reset() { *x = StateChallenge{} if protoimpl.UnsafeEnabled { mi := &file_verifiablecredstypes_bertyverifiablecreds_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *StateChallenge) String() string { return protoimpl.X.MessageStringOf(x) } func (*StateChallenge) ProtoMessage() {} func (x *StateChallenge) ProtoReflect() protoreflect.Message { mi := &file_verifiablecredstypes_bertyverifiablecreds_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use StateChallenge.ProtoReflect.Descriptor instead. func (*StateChallenge) Descriptor() ([]byte, []int) { return file_verifiablecredstypes_bertyverifiablecreds_proto_rawDescGZIP(), []int{0} } func (x *StateChallenge) GetTimestamp() []byte { if x != nil { return x.Timestamp } return nil } func (x *StateChallenge) GetNonce() []byte { if x != nil { return x.Nonce } return nil } func (x *StateChallenge) GetBertyLink() string { if x != nil { return x.BertyLink } return "" } func (x *StateChallenge) GetRedirectUri() string { if x != nil { return x.RedirectUri } return "" } func (x *StateChallenge) GetState() string { if x != nil { return x.State } return "" } // StateCode serialized and signed state used when requesting a code type StateCode struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Timestamp []byte `protobuf:"bytes,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` BertyLink string `protobuf:"bytes,2,opt,name=berty_link,json=bertyLink,proto3" json:"berty_link,omitempty"` CodeStrategy CodeStrategy `protobuf:"varint,3,opt,name=code_strategy,json=codeStrategy,proto3,enum=weshnet.account.v1.CodeStrategy" json:"code_strategy,omitempty"` Identifier string `protobuf:"bytes,4,opt,name=identifier,proto3" json:"identifier,omitempty"` Code string `protobuf:"bytes,5,opt,name=code,proto3" json:"code,omitempty"` RedirectUri string `protobuf:"bytes,6,opt,name=redirect_uri,json=redirectUri,proto3" json:"redirect_uri,omitempty"` State string `protobuf:"bytes,7,opt,name=state,proto3" json:"state,omitempty"` } func (x *StateCode) Reset() { *x = StateCode{} if protoimpl.UnsafeEnabled { mi := &file_verifiablecredstypes_bertyverifiablecreds_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *StateCode) String() string { return protoimpl.X.MessageStringOf(x) } func (*StateCode) ProtoMessage() {} func (x *StateCode) ProtoReflect() protoreflect.Message { mi := &file_verifiablecredstypes_bertyverifiablecreds_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use StateCode.ProtoReflect.Descriptor instead. func (*StateCode) Descriptor() ([]byte, []int) { return file_verifiablecredstypes_bertyverifiablecreds_proto_rawDescGZIP(), []int{1} } func (x *StateCode) GetTimestamp() []byte { if x != nil { return x.Timestamp } return nil } func (x *StateCode) GetBertyLink() string { if x != nil { return x.BertyLink } return "" } func (x *StateCode) GetCodeStrategy() CodeStrategy { if x != nil { return x.CodeStrategy } return CodeStrategy_CodeStrategyUndefined } func (x *StateCode) GetIdentifier() string { if x != nil { return x.Identifier } return "" } func (x *StateCode) GetCode() string { if x != nil { return x.Code } return "" } func (x *StateCode) GetRedirectUri() string { if x != nil { return x.RedirectUri } return "" } func (x *StateCode) GetState() string { if x != nil { return x.State } return "" } type AccountCryptoChallenge struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Challenge string `protobuf:"bytes,1,opt,name=challenge,proto3" json:"challenge,omitempty"` } func (x *AccountCryptoChallenge) Reset() { *x = AccountCryptoChallenge{} if protoimpl.UnsafeEnabled { mi := &file_verifiablecredstypes_bertyverifiablecreds_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *AccountCryptoChallenge) String() string { return protoimpl.X.MessageStringOf(x) } func (*AccountCryptoChallenge) ProtoMessage() {} func (x *AccountCryptoChallenge) ProtoReflect() protoreflect.Message { mi := &file_verifiablecredstypes_bertyverifiablecreds_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AccountCryptoChallenge.ProtoReflect.Descriptor instead. func (*AccountCryptoChallenge) Descriptor() ([]byte, []int) { return file_verifiablecredstypes_bertyverifiablecreds_proto_rawDescGZIP(), []int{2} } func (x *AccountCryptoChallenge) GetChallenge() string { if x != nil { return x.Challenge } return "" } var File_verifiablecredstypes_bertyverifiablecreds_proto protoreflect.FileDescriptor var file_verifiablecredstypes_bertyverifiablecreds_proto_rawDesc = []byte{ 0x0a, 0x2f, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x63, 0x72, 0x65, 0x64, 0x73, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x62, 0x65, 0x72, 0x74, 0x79, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x63, 0x72, 0x65, 0x64, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x12, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x76, 0x31, 0x22, 0x9c, 0x01, 0x0a, 0x0e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x65, 0x72, 0x74, 0x79, 0x5f, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x62, 0x65, 0x72, 0x74, 0x79, 0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x55, 0x72, 0x69, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0xfc, 0x01, 0x0a, 0x09, 0x53, 0x74, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x65, 0x72, 0x74, 0x79, 0x5f, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x62, 0x65, 0x72, 0x74, 0x79, 0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x45, 0x0a, 0x0d, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x64, 0x65, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0c, 0x63, 0x6f, 0x64, 0x65, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x55, 0x72, 0x69, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x36, 0x0a, 0x16, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x43, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x2a, 0x58, 0x0a, 0x08, 0x46, 0x6c, 0x6f, 0x77, 0x54, 0x79, 0x70, 0x65, 0x12, 0x15, 0x0a, 0x11, 0x46, 0x6c, 0x6f, 0x77, 0x54, 0x79, 0x70, 0x65, 0x55, 0x6e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x46, 0x6c, 0x6f, 0x77, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x46, 0x6c, 0x6f, 0x77, 0x54, 0x79, 0x70, 0x65, 0x41, 0x75, 0x74, 0x68, 0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x46, 0x6c, 0x6f, 0x77, 0x54, 0x79, 0x70, 0x65, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x10, 0x03, 0x2a, 0x7b, 0x0a, 0x0c, 0x43, 0x6f, 0x64, 0x65, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x19, 0x0a, 0x15, 0x43, 0x6f, 0x64, 0x65, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x55, 0x6e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x10, 0x00, 0x12, 0x17, 0x0a, 0x13, 0x43, 0x6f, 0x64, 0x65, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x36, 0x44, 0x69, 0x67, 0x69, 0x74, 0x73, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x43, 0x6f, 0x64, 0x65, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x31, 0x30, 0x43, 0x68, 0x61, 0x72, 0x73, 0x10, 0x02, 0x12, 0x1e, 0x0a, 0x19, 0x43, 0x6f, 0x64, 0x65, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x4d, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x36, 0x5a, 0x65, 0x72, 0x6f, 0x65, 0x73, 0x10, 0xe7, 0x07, 0x42, 0x30, 0x5a, 0x2e, 0x62, 0x65, 0x72, 0x74, 0x79, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x2f, 0x77, 0x65, 0x73, 0x68, 0x6e, 0x65, 0x74, 0x2f, 0x76, 0x32, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x63, 0x72, 0x65, 0x64, 0x73, 0x74, 0x79, 0x70, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( file_verifiablecredstypes_bertyverifiablecreds_proto_rawDescOnce sync.Once file_verifiablecredstypes_bertyverifiablecreds_proto_rawDescData = file_verifiablecredstypes_bertyverifiablecreds_proto_rawDesc ) func file_verifiablecredstypes_bertyverifiablecreds_proto_rawDescGZIP() []byte { file_verifiablecredstypes_bertyverifiablecreds_proto_rawDescOnce.Do(func() { file_verifiablecredstypes_bertyverifiablecreds_proto_rawDescData = protoimpl.X.CompressGZIP(file_verifiablecredstypes_bertyverifiablecreds_proto_rawDescData) }) return file_verifiablecredstypes_bertyverifiablecreds_proto_rawDescData } var file_verifiablecredstypes_bertyverifiablecreds_proto_enumTypes = make([]protoimpl.EnumInfo, 2) var file_verifiablecredstypes_bertyverifiablecreds_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_verifiablecredstypes_bertyverifiablecreds_proto_goTypes = []any{ (FlowType)(0), // 0: weshnet.account.v1.FlowType (CodeStrategy)(0), // 1: weshnet.account.v1.CodeStrategy (*StateChallenge)(nil), // 2: weshnet.account.v1.StateChallenge (*StateCode)(nil), // 3: weshnet.account.v1.StateCode (*AccountCryptoChallenge)(nil), // 4: weshnet.account.v1.AccountCryptoChallenge } var file_verifiablecredstypes_bertyverifiablecreds_proto_depIdxs = []int32{ 1, // 0: weshnet.account.v1.StateCode.code_strategy:type_name -> weshnet.account.v1.CodeStrategy 1, // [1:1] is the sub-list for method output_type 1, // [1:1] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name } func init() { file_verifiablecredstypes_bertyverifiablecreds_proto_init() } func file_verifiablecredstypes_bertyverifiablecreds_proto_init() { if File_verifiablecredstypes_bertyverifiablecreds_proto != nil { return } if !protoimpl.UnsafeEnabled { file_verifiablecredstypes_bertyverifiablecreds_proto_msgTypes[0].Exporter = func(v any, i int) any { switch v := v.(*StateChallenge); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_verifiablecredstypes_bertyverifiablecreds_proto_msgTypes[1].Exporter = func(v any, i int) any { switch v := v.(*StateCode); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_verifiablecredstypes_bertyverifiablecreds_proto_msgTypes[2].Exporter = func(v any, i int) any { switch v := v.(*AccountCryptoChallenge); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_verifiablecredstypes_bertyverifiablecreds_proto_rawDesc, NumEnums: 2, NumMessages: 3, NumExtensions: 0, NumServices: 0, }, GoTypes: file_verifiablecredstypes_bertyverifiablecreds_proto_goTypes, DependencyIndexes: file_verifiablecredstypes_bertyverifiablecreds_proto_depIdxs, EnumInfos: file_verifiablecredstypes_bertyverifiablecreds_proto_enumTypes, MessageInfos: file_verifiablecredstypes_bertyverifiablecreds_proto_msgTypes, }.Build() File_verifiablecredstypes_bertyverifiablecreds_proto = out.File file_verifiablecredstypes_bertyverifiablecreds_proto_rawDesc = nil file_verifiablecredstypes_bertyverifiablecreds_proto_goTypes = nil file_verifiablecredstypes_bertyverifiablecreds_proto_depIdxs = nil } ================================================ FILE: scenario_test.go ================================================ package weshnet_test import ( "bytes" "context" "encoding/base64" "fmt" "io" "os" "sync" "sync/atomic" "testing" "time" mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/goleak" "go.uber.org/zap" "google.golang.org/protobuf/proto" weshnet "berty.tech/weshnet/v2" "berty.tech/weshnet/v2/pkg/errcode" "berty.tech/weshnet/v2/pkg/protocoltypes" "berty.tech/weshnet/v2/pkg/testutil" ) type testCase struct { Name string NumberOfClient int ConnectFunc weshnet.ConnectTestingProtocolFunc Speed testutil.Speed Stability testutil.Stability Timeout time.Duration } type testFunc func(context.Context, *testing.T, ...*weshnet.TestingProtocol) // Tests func TestScenario_CreateMultiMemberGroup(t *testing.T) { cases := []testCase{ {"2 clients/connectAll", 2, weshnet.ConnectAll, testutil.Fast, testutil.Flappy, time.Second * 10}, {"3 clients/connectAll", 3, weshnet.ConnectAll, testutil.Fast, testutil.Flappy, time.Second * 10}, {"3 clients/connectInLine", 3, weshnet.ConnectInLine, testutil.Fast, testutil.Flappy, time.Second * 10}, {"5 clients/connectAll", 5, weshnet.ConnectAll, testutil.Slow, testutil.Flappy, time.Second * 20}, {"5 clients/connectInLine", 5, weshnet.ConnectInLine, testutil.Slow, testutil.Flappy, time.Second * 20}, {"8 clients/connectAll", 8, weshnet.ConnectAll, testutil.Slow, testutil.Flappy, time.Second * 60}, {"8 clients/connectInLine", 8, weshnet.ConnectInLine, testutil.Slow, testutil.Flappy, time.Second * 60}, {"10 clients/connectAll", 10, weshnet.ConnectAll, testutil.Slow, testutil.Flappy, time.Second * 90}, {"10 clients/connectInLine", 10, weshnet.ConnectInLine, testutil.Slow, testutil.Flappy, time.Second * 90}, } testingScenario(t, cases, func(ctx context.Context, t *testing.T, tps ...*weshnet.TestingProtocol) { createMultiMemberGroup(ctx, t, tps...) }) } func TestScenario_MessageMultiMemberGroup(t *testing.T) { cases := []testCase{ {"2 clients/connectAll", 2, weshnet.ConnectAll, testutil.Fast, testutil.Flappy, time.Second * 10}, {"3 clients/connectAll", 3, weshnet.ConnectAll, testutil.Fast, testutil.Flappy, time.Second * 10}, {"3 clients/connectInLine", 3, weshnet.ConnectInLine, testutil.Fast, testutil.Flappy, time.Second * 10}, {"5 clients/connectAll", 5, weshnet.ConnectAll, testutil.Slow, testutil.Flappy, time.Second * 20}, {"5 clients/connectInLine", 5, weshnet.ConnectInLine, testutil.Slow, testutil.Flappy, time.Second * 20}, {"8 clients/connectAll", 8, weshnet.ConnectAll, testutil.Slow, testutil.Flappy, time.Second * 40}, {"8 clients/connectInLine", 8, weshnet.ConnectInLine, testutil.Slow, testutil.Flappy, time.Second * 40}, {"10 clients/connectAll", 10, weshnet.ConnectAll, testutil.Slow, testutil.Flappy, time.Second * 90}, {"10 clients/connectInLine", 10, weshnet.ConnectInLine, testutil.Slow, testutil.Flappy, time.Second * 90}, } testingScenario(t, cases, func(ctx context.Context, t *testing.T, tps ...*weshnet.TestingProtocol) { // Create MultiMember Group groupID := createMultiMemberGroup(ctx, t, tps...) // Each member sends 3 messages on MultiMember Group messages := []string{"test1", "test2", "test3"} sendMessageOnGroup(ctx, t, tps, tps, groupID, messages) }) } func TestScenario_GroupDeviceStatusOnMultiMemberGroup(t *testing.T) { cases := []testCase{ {"2 clients/connectAll", 2, weshnet.ConnectAll, testutil.Fast, testutil.Flappy, time.Second * 10}, {"3 clients/connectAll", 3, weshnet.ConnectAll, testutil.Fast, testutil.Flappy, time.Second * 10}, {"5 clients/connectAll", 5, weshnet.ConnectAll, testutil.Slow, testutil.Flappy, time.Second * 20}, {"8 clients/connectAll", 8, weshnet.ConnectAll, testutil.Slow, testutil.Flappy, time.Second * 60}, {"10 clients/connectAll", 10, weshnet.ConnectAll, testutil.Slow, testutil.Flappy, time.Second * 90}, } testingScenario(t, cases, func(ctx context.Context, t *testing.T, tps ...*weshnet.TestingProtocol) { // Create MultiMember Group groupID := createMultiMemberGroup(ctx, t, tps...) testGroupDeviceStatus(ctx, t, groupID, tps...) }) } func testGroupDeviceStatus(ctx context.Context, t *testing.T, groupID []byte, tps ...*weshnet.TestingProtocol) { ntps := len(tps) // Get group device status { testutil.LogTree(t, "Get Group Device Status", 1, true) start := time.Now() wg := sync.WaitGroup{} statusReceivedLock := sync.Mutex{} statusReceived := make([]map[string]struct{}, ntps) wg.Add(ntps) nSuccess := int64(0) for i := range tps { go func(i int) { tp := tps[i] defer wg.Done() statusReceived[i] = map[string]struct{}{} ctx, cancel := context.WithCancel(ctx) defer cancel() sub, inErr := tp.Client.GroupDeviceStatus(ctx, &protocoltypes.GroupDeviceStatus_Request{ GroupPk: groupID, }) if inErr != nil { assert.NoError(t, inErr, fmt.Sprintf("error for client %d", i)) return } for { evt, inErr := sub.Recv() if inErr != nil { if inErr != io.EOF { assert.NoError(t, inErr, fmt.Sprintf("error for client %d", i)) } break } assert.Equal(t, evt.Type, protocoltypes.GroupDeviceStatus_TypePeerConnected) connected := &protocoltypes.GroupDeviceStatus_Reply_PeerConnected{} err := proto.Unmarshal(evt.Event, connected) assert.NoError(t, err, fmt.Sprintf("Unmarshal error for client %d", i)) statusReceivedLock.Lock() statusReceived[i][connected.PeerId] = struct{}{} done := len(statusReceived[i]) == ntps-1 statusReceivedLock.Unlock() if done { n := atomic.AddInt64(&nSuccess, 1) got := fmt.Sprintf("%d/%d", n, ntps) tps[i].Opts.Logger.Debug("received all group device status", zap.String("ok", got)) return } } }(i) } wg.Wait() statusReceivedLock.Lock() ok := true for i := range statusReceived { if !assert.Equal(t, ntps-1, len(statusReceived[i]), fmt.Sprintf("mismatch for client %d", i)) { ok = false } } require.True(t, ok) statusReceivedLock.Unlock() testutil.LogTree(t, "duration: %s", 1, false, time.Since(start)) } } // //func TestScenario_MessageMultiMemberGroup2(t *testing.T) { // cases := []testCase{ // {"2 clients/connectAll", 2, ConnectAll, testutil.Fast, testutil.Stable, time.Second * 60}, // } // // testingScenario(t, cases, func(ctx context.Context, t *testing.T, tps ...*TestingProtocol) { // // Create MultiMember Group // groupID := createMultiMemberGroup(ctx, t, tps...) // // const messageCount = 100 // // Each member sends 3 messages on MultiMember Group // messages := make([]string, messageCount) // for i := 0; i < messageCount; i++ { // messages[i] = fmt.Sprintf("test%d", i) // } // // sendMessageOnGroup(ctx, t, tps, tps, groupID, messages) // }) //} func TestScenario_MessageSeveralMultiMemberGroups(t *testing.T) { const ngroup = 3 cases := []testCase{ {"2 clients/connectAll", 2, weshnet.ConnectAll, testutil.Fast, testutil.Flappy, time.Second * 20}, {"3 clients/connectAll", 3, weshnet.ConnectAll, testutil.Fast, testutil.Flappy, time.Second * 20}, {"3 clients/connectInLine", 3, weshnet.ConnectInLine, testutil.Fast, testutil.Flappy, time.Second * 20}, {"5 clients/connectAll", 5, weshnet.ConnectAll, testutil.Slow, testutil.Flappy, time.Second * 60}, {"5 clients/connectInLine", 5, weshnet.ConnectInLine, testutil.Slow, testutil.Flappy, time.Second * 60}, {"8 clients/connectAll", 8, weshnet.ConnectAll, testutil.Slow, testutil.Flappy, time.Second * 180}, {"8 clients/connectInLine", 8, weshnet.ConnectInLine, testutil.Slow, testutil.Flappy, time.Second * 180}, {"10 clients/connectAll", 10, weshnet.ConnectAll, testutil.Slow, testutil.Flappy, time.Second * 300}, {"10 clients/connectInLine", 10, weshnet.ConnectInLine, testutil.Slow, testutil.Flappy, time.Second * 300}, } testingScenario(t, cases, func(ctx context.Context, t *testing.T, tps ...*weshnet.TestingProtocol) { for i := 0; i < ngroup; i++ { t.Logf("===== MultiMember Group #%d =====", i+1) // Create MultiMember Group groupID := createMultiMemberGroup(ctx, t, tps...) // Each member sends 3 messages on MultiMember Group messages := []string{"test1", "test2", "test3"} sendMessageOnGroup(ctx, t, tps, tps, groupID, messages) } }) } func TestScenario_AddContact(t *testing.T) { cases := []testCase{ {"2 clients/connectAll", 2, weshnet.ConnectAll, testutil.Fast, testutil.Flappy, time.Second * 20}, {"3 clients/connectAll", 3, weshnet.ConnectAll, testutil.Fast, testutil.Flappy, time.Second * 20}, {"5 clients/connectAll", 5, weshnet.ConnectAll, testutil.Slow, testutil.Flappy, time.Second * 30}, {"8 clients/connectAll", 8, weshnet.ConnectAll, testutil.Slow, testutil.Flappy, time.Second * 40}, {"10 clients/connectAll", 10, weshnet.ConnectAll, testutil.Slow, testutil.Flappy, time.Second * 60}, } testingScenario(t, cases, func(ctx context.Context, t *testing.T, tps ...*weshnet.TestingProtocol) { addAsContact(ctx, t, tps, tps) }) } func TestScenario_MessageContactGroup(t *testing.T) { cases := []testCase{ {"2 clients/connectAll", 2, weshnet.ConnectAll, testutil.Fast, testutil.Flappy, time.Second * 20}, {"3 clients/connectAll", 3, weshnet.ConnectAll, testutil.Fast, testutil.Flappy, time.Second * 20}, {"5 clients/connectAll", 5, weshnet.ConnectAll, testutil.Slow, testutil.Flappy, time.Second * 30}, {"8 clients/connectAll", 8, weshnet.ConnectAll, testutil.Slow, testutil.Broken, time.Second * 40}, {"10 clients/connectAll", 10, weshnet.ConnectAll, testutil.Slow, testutil.Broken, time.Second * 60}, } testingScenario(t, cases, func(ctx context.Context, t *testing.T, tps ...*weshnet.TestingProtocol) { // Add accounts as contacts addAsContact(ctx, t, tps, tps) // Send messages between all accounts on contact groups messages := []string{"test1", "test2", "test3"} sendMessageToContact(ctx, t, messages, tps) }) } func TestScenario_MessageAccountGroup(t *testing.T) { cases := []testCase{ {"1 client/connectAll", 1, weshnet.ConnectAll, testutil.Fast, testutil.Stable, time.Second * 10}, } testingScenario(t, cases, func(ctx context.Context, t *testing.T, tps ...*weshnet.TestingProtocol) { // Get account config config, err := tps[0].Client.ServiceGetConfiguration(ctx, &protocoltypes.ServiceGetConfiguration_Request{}) require.NoError(t, err) require.NotNil(t, config) // Send messages on account group messages := []string{"test1", "test2", "test3"} sendMessageOnGroup(ctx, t, tps, tps, config.AccountGroupPk, messages) }) } func TestScenario_MessageAccountGroup_NonMocked(t *testing.T) { cases := []testCase{ {"1 client/connectAll", 1, weshnet.ConnectAll, testutil.Fast, testutil.Stable, time.Second * 10}, } testingScenarioNonMocked(t, cases, func(ctx context.Context, t *testing.T, tps ...*weshnet.TestingProtocol) { // Get account config config, err := tps[0].Client.ServiceGetConfiguration(ctx, &protocoltypes.ServiceGetConfiguration_Request{}) require.NoError(t, err) require.NotNil(t, config) // Send messages on account group messages := []string{"test1", "test2", "test3"} sendMessageOnGroup(ctx, t, tps, tps, config.AccountGroupPk, messages) }) } func TestScenario_MessageAccountAndMultiMemberGroups(t *testing.T) { cases := []testCase{ {"2 clients/connectAll", 2, weshnet.ConnectAll, testutil.Fast, testutil.Broken, time.Second * 10}, {"3 clients/connectAll", 3, weshnet.ConnectAll, testutil.Fast, testutil.Broken, time.Second * 10}, {"3 clients/connectInLine", 3, weshnet.ConnectInLine, testutil.Fast, testutil.Broken, time.Second * 10}, {"5 clients/connectAll", 5, weshnet.ConnectAll, testutil.Slow, testutil.Broken, time.Second * 20}, {"5 clients/connectInLine", 5, weshnet.ConnectInLine, testutil.Slow, testutil.Broken, time.Second * 20}, {"8 clients/connectAll", 8, weshnet.ConnectAll, testutil.Slow, testutil.Broken, time.Second * 30}, {"8 clients/connectInLine", 8, weshnet.ConnectInLine, testutil.Slow, testutil.Broken, time.Second * 30}, {"10 clients/connectAll", 10, weshnet.ConnectAll, testutil.Slow, testutil.Broken, time.Second * 40}, {"10 clients/connectInLine", 10, weshnet.ConnectInLine, testutil.Slow, testutil.Broken, time.Second * 40}, } testingScenario(t, cases, func(ctx context.Context, t *testing.T, tps ...*weshnet.TestingProtocol) { t.Log("===== Send Messages on MultiMember Group =====") // Create MultiMember Group mmGroup := createMultiMemberGroup(ctx, t, tps...) // Each member sends 3 messages on MultiMember Group messages := []string{"test1", "test2", "test3"} sendMessageOnGroup(ctx, t, tps, tps, mmGroup, messages) t.Log("===== Send Messages on Account Group =====") // Send messages on account groups for _, account := range tps { // Get account config config, err := account.Client.ServiceGetConfiguration(ctx, &protocoltypes.ServiceGetConfiguration_Request{}) require.NoError(t, err) require.NotNil(t, config) // Send messages on account group messages = []string{"account1", "account2", "account3"} sendMessageOnGroup(ctx, t, []*weshnet.TestingProtocol{account}, []*weshnet.TestingProtocol{account}, config.AccountGroupPk, messages) } t.Log("===== Send Messages again on MultiMember Group =====") // Each member sends 3 messages on MultiMember Group messages = []string{"test4", "test5", "test6"} sendMessageOnGroup(ctx, t, tps, tps, mmGroup, messages) }) } func TestScenario_MessageAccountAndContactGroups(t *testing.T) { cases := []testCase{ {"2 clients/connectAll", 2, weshnet.ConnectAll, testutil.Fast, testutil.Broken, time.Second * 10}, {"3 clients/connectAll", 3, weshnet.ConnectAll, testutil.Fast, testutil.Broken, time.Second * 10}, {"5 clients/connectAll", 5, weshnet.ConnectAll, testutil.Slow, testutil.Broken, time.Second * 20}, {"8 clients/connectAll", 8, weshnet.ConnectAll, testutil.Slow, testutil.Broken, time.Second * 30}, {"10 clients/connectAll", 10, weshnet.ConnectAll, testutil.Slow, testutil.Broken, time.Second * 40}, } testingScenario(t, cases, func(ctx context.Context, t *testing.T, tps ...*weshnet.TestingProtocol) { t.Log("===== Send Messages on Contact Group =====") // Add accounts as contacts addAsContact(ctx, t, tps, tps) // Send messages between all accounts on contact groups messages := []string{"contact1", "contact2", "contact3"} sendMessageToContact(ctx, t, messages, tps) t.Log("===== Send Messages on Account Group =====") // Send messages on account groups for _, account := range tps { // Get account config config, err := account.Client.ServiceGetConfiguration(ctx, &protocoltypes.ServiceGetConfiguration_Request{}) require.NoError(t, err) require.NotNil(t, config) // Send messages on account group messages = []string{"account1", "account2", "account3"} sendMessageOnGroup(ctx, t, []*weshnet.TestingProtocol{account}, []*weshnet.TestingProtocol{account}, config.AccountGroupPk, messages) } t.Log("===== Send Messages again on Contact Group =====") // Send messages between all accounts on contact groups messages = []string{"contact4", "contact5", "contact6"} sendMessageToContact(ctx, t, messages, tps) }) } // Helpers func testingScenario(t *testing.T, tcs []testCase, tf testFunc) { if os.Getenv("WITH_GOLEAK") == "1" { defer goleak.VerifyNone(t, goleak.IgnoreTopFunction("github.com/syndtr/goleveldb/leveldb.(*DB).mpoolDrain"), // inherited from one of the imports (init) goleak.IgnoreTopFunction("github.com/ipfs/go-log/writer.(*MirrorWriter).logRoutine"), // inherited from one of the imports (init) goleak.IgnoreTopFunction("github.com/jbenet/goprocess/periodic.callOnTicker.func1"), // inherited from github.com/ipfs/kubo/core.NewNode goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), // inherited from github.com/ipfs/kubo/core.NewNode) goleak.IgnoreTopFunction("github.com/desertbit/timer.timerRoutine"), // inherited from github.com/ipfs/kubo/core.NewNode) goleak.IgnoreTopFunction("go.opentelemetry.io/otel/instrumentation/grpctrace.wrapClientStream.func1"), goleak.IgnoreTopFunction("go.opentelemetry.io/otel/instrumentation/grpctrace.StreamClientInterceptor.func1.1"), ) } for _, tc := range tcs { t.Run(tc.Name, func(t *testing.T) { testutil.FilterStabilityAndSpeed(t, tc.Stability, tc.Speed) ctx, cancel := context.WithCancel(context.Background()) defer cancel() logger, cleanup := testutil.Logger(t) defer cleanup() mn := mocknet.New() defer mn.Close() opts := weshnet.TestingOpts{ Mocknet: mn, Logger: logger, ConnectFunc: tc.ConnectFunc, } tps, cleanup := weshnet.NewTestingProtocolWithMockedPeers(ctx, t, &opts, nil, tc.NumberOfClient) defer cleanup() var cctx context.Context if tc.Timeout > 0 { cctx, cancel = context.WithTimeout(ctx, tc.Timeout) } else { cctx, cancel = context.WithCancel(ctx) } tf(cctx, t, tps...) cancel() }) } } func testingScenarioNonMocked(t *testing.T, tcs []testCase, tf testFunc) { if os.Getenv("WITH_GOLEAK") == "1" { defer goleak.VerifyNone(t, goleak.IgnoreTopFunction("github.com/syndtr/goleveldb/leveldb.(*DB).mpoolDrain"), // inherited from one of the imports (init) goleak.IgnoreTopFunction("github.com/ipfs/go-log/writer.(*MirrorWriter).logRoutine"), // inherited from one of the imports (init) goleak.IgnoreTopFunction("github.com/jbenet/goprocess/periodic.callOnTicker.func1"), // inherited from github.com/ipfs/kubo/core.NewNode goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), // inherited from github.com/ipfs/kubo/core.NewNode) goleak.IgnoreTopFunction("github.com/desertbit/timer.timerRoutine"), // inherited from github.com/ipfs/kubo/core.NewNode) goleak.IgnoreTopFunction("go.opentelemetry.io/otel/instrumentation/grpctrace.wrapClientStream.func1"), goleak.IgnoreTopFunction("go.opentelemetry.io/otel/instrumentation/grpctrace.StreamClientInterceptor.func1.1"), ) } for _, tc := range tcs { t.Run(tc.Name, func(t *testing.T) { testutil.FilterStabilityAndSpeed(t, tc.Stability, tc.Speed) ctx, cancel := context.WithCancel(context.Background()) defer cancel() logger, cleanup := testutil.Logger(t) defer cleanup() mn := mocknet.New() defer mn.Close() opts := weshnet.TestingOpts{ Mocknet: mn, Logger: logger, ConnectFunc: tc.ConnectFunc, } tps, cleanup := weshnet.NewTestingProtocolWithMockedPeers(ctx, t, &opts, nil, tc.NumberOfClient) defer cleanup() var cctx context.Context if tc.Timeout > 0 { cctx, cancel = context.WithTimeout(ctx, tc.Timeout) } else { cctx, cancel = context.WithCancel(ctx) } tf(cctx, t, tps...) cancel() }) } } func createMultiMemberGroup(ctx context.Context, t *testing.T, tps ...*weshnet.TestingProtocol) (groupID []byte) { return weshnet.CreateMultiMemberGroupInstance(ctx, t, tps...).PublicKey } func addAsContact(ctx context.Context, t *testing.T, senders, receivers []*weshnet.TestingProtocol) { testutil.LogTree(t, "Add Senders/Receivers as Contact", 0, true) start := time.Now() var sendDuration, receiveDuration, acceptDuration, activateDuration time.Duration for i, sender := range senders { for _, receiver := range receivers { substart := time.Now() // Get sender/receiver configs senderCfg, err := sender.Client.ServiceGetConfiguration(ctx, &protocoltypes.ServiceGetConfiguration_Request{}) require.NoError(t, err) require.NotNil(t, senderCfg) receiverCfg, err := receiver.Client.ServiceGetConfiguration(ctx, &protocoltypes.ServiceGetConfiguration_Request{}) require.NoError(t, err) require.NotNil(t, receiverCfg) // Setup receiver's shareable contact var receiverRDVSeed []byte crf, err := receiver.Client.ContactRequestReference(ctx, &protocoltypes.ContactRequestReference_Request{}) if err != nil || !crf.Enabled || len(crf.PublicRendezvousSeed) == 0 { _, err = receiver.Client.ContactRequestEnable(ctx, &protocoltypes.ContactRequestEnable_Request{}) require.NoError(t, err) receiverRDV, err := receiver.Client.ContactRequestResetReference(ctx, &protocoltypes.ContactRequestResetReference_Request{}) require.NoError(t, err) require.NotNil(t, receiverRDV) receiverRDVSeed = receiverRDV.PublicRendezvousSeed } else { receiverRDVSeed = crf.PublicRendezvousSeed } receiverSharableContact := &protocoltypes.ShareableContact{ Pk: receiverCfg.AccountPk, PublicRendezvousSeed: receiverRDVSeed, } // Sender sends contact request _, err = sender.Client.ContactRequestSend(ctx, &protocoltypes.ContactRequestSend_Request{ Contact: receiverSharableContact, }) // Check if sender and receiver are the same account, should return the right error and skip if bytes.Equal(senderCfg.AccountPk, receiverCfg.AccountPk) { require.Equal(t, errcode.LastCode(err), errcode.ErrCode_ErrContactRequestSameAccount) continue } // Check if contact request was already sent, should return right error and skip receiverWasSender := false for j := 0; j < i; j++ { if senders[j] == receiver { receiverWasSender = true } } senderWasReceiver := false if receiverWasSender { for _, r := range receivers { if r == sender { senderWasReceiver = true } } } if receiverWasSender && senderWasReceiver { require.Equal(t, errcode.LastCode(err), errcode.ErrCode_ErrContactRequestContactAlreadyAdded) continue } // No other error should occur require.NoError(t, err) sendDuration += time.Since(substart) substart = time.Now() // Receiver subscribes to handle incoming contact request subCtx, subCancel := context.WithCancel(ctx) subReceiver, err := receiver.Client.GroupMetadataList(subCtx, &protocoltypes.GroupMetadataList_Request{ GroupPk: receiverCfg.AccountGroupPk, }) require.NoError(t, err) found := false // Receiver waits for valid contact request coming from sender for { evt, err := subReceiver.Recv() if err == io.EOF || subReceiver.Context().Err() != nil { break } require.NoError(t, err) if evt == nil || evt.Metadata.EventType != protocoltypes.EventType_EventTypeAccountContactRequestIncomingReceived { continue } req := &protocoltypes.AccountContactRequestIncomingReceived{} err = proto.Unmarshal(evt.Event, req) require.NoError(t, err) if bytes.Equal(senderCfg.AccountPk, req.ContactPk) { found = true break } } subCancel() require.True(t, found) receiveDuration += time.Since(substart) substart = time.Now() // Receiver accepts contact request _, err = receiver.Client.ContactRequestAccept(ctx, &protocoltypes.ContactRequestAccept_Request{ ContactPk: senderCfg.AccountPk, }) require.NoError(t, err) acceptDuration += time.Since(substart) substart = time.Now() // Both receiver and sender activate the contact group grpInfo, err := sender.Client.GroupInfo(ctx, &protocoltypes.GroupInfo_Request{ ContactPk: receiverCfg.AccountPk, }) require.NoError(t, err) _, err = sender.Client.ActivateGroup(ctx, &protocoltypes.ActivateGroup_Request{ GroupPk: grpInfo.Group.PublicKey, }) require.NoError(t, err) grpInfo2, err := receiver.Client.GroupInfo(ctx, &protocoltypes.GroupInfo_Request{ ContactPk: senderCfg.AccountPk, }) require.NoError(t, err) require.Equal(t, grpInfo.Group.PublicKey, grpInfo2.Group.PublicKey) _, err = receiver.Client.ActivateGroup(ctx, &protocoltypes.ActivateGroup_Request{ GroupPk: grpInfo2.Group.PublicKey, }) require.NoError(t, err) activateDuration += time.Since(substart) } } testutil.LogTree(t, "Send Contact Requests", 1, true) testutil.LogTree(t, "duration: %s", 1, false, sendDuration) testutil.LogTree(t, "Receive Contact Requests", 1, true) testutil.LogTree(t, "duration: %s", 1, false, receiveDuration) testutil.LogTree(t, "Accept Contact Requests", 1, true) testutil.LogTree(t, "duration: %s", 1, false, acceptDuration) testutil.LogTree(t, "Activate Contact Groups", 1, true) testutil.LogTree(t, "duration: %s", 1, false, activateDuration) testutil.LogTree(t, "duration: %s", 0, false, time.Since(start)) } func getContactGroup(ctx context.Context, t *testing.T, source *weshnet.TestingProtocol, contact *weshnet.TestingProtocol) *protocoltypes.GroupInfo_Reply { // Get contact group contactGroup, err := source.Client.GroupInfo(ctx, &protocoltypes.GroupInfo_Request{ ContactPk: getAccountPubKey(t, contact), }) require.NoError(t, err) require.NotNil(t, contactGroup) return contactGroup } func sendMessageToContact(ctx context.Context, t *testing.T, messages []string, tps []*weshnet.TestingProtocol) { for _, sender := range tps { for _, receiver := range tps { // Don't try to send messages to itself using contact group if sender == receiver { continue } // Get contact group contactGroup := getContactGroup(ctx, t, sender, receiver) // Send messages on contact group sendMessageOnGroup(ctx, t, []*weshnet.TestingProtocol{sender}, []*weshnet.TestingProtocol{receiver}, contactGroup.Group.PublicKey, messages) } } } func sendMessageOnGroup(ctx context.Context, t *testing.T, senders, receivers []*weshnet.TestingProtocol, groupPK []byte, messages []string) { testutil.LogTree(t, "Send, Receive and List Messages", 0, true) start := time.Now() // Setup expectedMessages map expectedMessages := map[string]struct{}{} expectedMessagesCount := len(messages) * len(senders) expectedMessagesLock := sync.Mutex{} for _, message := range messages { for _, sender := range senders { expectedMessage := getAccountB64PubKey(t, sender) + " - " + message expectedMessages[expectedMessage] = struct{}{} } } // Setup map to check expected messages reception subReceivedMessages := map[string]map[string]bool{} subReceivedMessagesCount := map[string]int{} listReceivedMessages := map[string]map[string]bool{} listReceivedMessagesCount := map[string]int{} for _, receiver := range receivers { subReceiverMap := map[string]bool{} listReceiverMap := map[string]bool{} for expectedMessage := range expectedMessages { subReceiverMap[expectedMessage] = false listReceiverMap[expectedMessage] = false } receiverID := getAccountB64PubKey(t, receiver) subReceivedMessages[receiverID] = subReceiverMap listReceivedMessages[receiverID] = listReceiverMap subReceivedMessagesCount[receiverID] = 0 listReceivedMessagesCount[receiverID] = 0 } receivedMessagesLock := sync.Mutex{} // Senders send all expected messages { testutil.LogTree(t, "Senders Send Messages", 1, true) start := time.Now() for _, sender := range senders { senderID := getAccountB64PubKey(t, sender) for _, message := range messages { _, err := sender.Client.AppMessageSend(ctx, &protocoltypes.AppMessageSend_Request{ GroupPk: groupPK, Payload: []byte(senderID + " - " + message), }) require.NoError(t, err) } } testutil.LogTree(t, "duration: %s", 1, false, time.Since(start)) } // Receivers receive all expected messages { testutil.LogTree(t, "Receivers Receive Messages (subscription)", 1, true) start := time.Now() var wg sync.WaitGroup wg.Add(len(receivers)) for _, receiver := range receivers { // Subscribe receivers to wait for incoming messages go func(receiver *weshnet.TestingProtocol) { subCtx, subCancel := context.WithCancel(ctx) defer subCancel() defer wg.Done() sub, err := receiver.Client.GroupMessageList(subCtx, &protocoltypes.GroupMessageList_Request{ GroupPk: groupPK, }) if !assert.NoError(t, err) { return } receiverID := getAccountB64PubKey(t, receiver) for { if subCtx.Err() != nil { return } // Receive message res, err := sub.Recv() if err == io.EOF { return } if !assert.NoError(t, err) { continue } // Check if received message was expected expectedMessagesLock.Lock() _, expected := expectedMessages[string(res.Message)] expectedMessagesLock.Unlock() if !expected { continue } // Check if message was already received receivedMessagesLock.Lock() alreadyReceived := subReceivedMessages[receiverID][string(res.Message)] if alreadyReceived { receivedMessagesLock.Unlock() continue } // Mark message as received subReceivedMessages[receiverID][string(res.Message)] = true subReceivedMessagesCount[receiverID]++ // Return if all expected messages were received if subReceivedMessagesCount[receiverID] == expectedMessagesCount { receivedMessagesLock.Unlock() return } receivedMessagesLock.Unlock() } }(receiver) } // Wait that all receivers received messages wg.Wait() // Check if everything is ok for _, receiver := range receivers { receiverID := getAccountB64PubKey(t, receiver) assert.Equal(t, expectedMessagesCount, subReceivedMessagesCount[receiverID]) } testutil.LogTree(t, "duration: %s", 1, false, time.Since(start)) } // Receivers list all expected messages { testutil.LogTree(t, "Receivers List Messages (store)", 1, true) start := time.Now() var wg sync.WaitGroup wg.Add(len(receivers)) for _, receiver := range receivers { // Subscribe receivers to wait for incoming messages go func(receiver *weshnet.TestingProtocol) { subCtx, subCancel := context.WithCancel(ctx) defer subCancel() defer wg.Done() req := protocoltypes.GroupMessageList_Request{ GroupPk: groupPK, UntilNow: true, } ml, err := receiver.Client.GroupMessageList(subCtx, &req) if !assert.NoError(t, err) { return } receiverID := getAccountB64PubKey(t, receiver) for { if subCtx.Err() != nil { return } // Receive message res, err := ml.Recv() if err == io.EOF { return } if !assert.NoError(t, err) { continue } // Check if received message was expected expectedMessagesLock.Lock() _, expected := expectedMessages[string(res.Message)] expectedMessagesLock.Unlock() if !expected { continue } // Check if message was already received receivedMessagesLock.Lock() alreadyReceived := listReceivedMessages[receiverID][string(res.Message)] if alreadyReceived { receivedMessagesLock.Unlock() continue } // Mark message as received listReceivedMessages[receiverID][string(res.Message)] = true listReceivedMessagesCount[receiverID]++ // Return if all expected messages were received if listReceivedMessagesCount[receiverID] == expectedMessagesCount { receivedMessagesLock.Unlock() return } receivedMessagesLock.Unlock() } }(receiver) } // Wait that all receivers listed messages wg.Wait() // Check if everything is ok for _, receiver := range receivers { receiverID := getAccountB64PubKey(t, receiver) assert.Equal(t, expectedMessagesCount, listReceivedMessagesCount[receiverID]) } testutil.LogTree(t, "duration: %s", 1, false, time.Since(start)) } testutil.LogTree(t, "duration: %s", 0, false, time.Since(start)) } func getAccountPubKey(t *testing.T, tp *weshnet.TestingProtocol) []byte { t.Helper() _, accMemberDevice, err := tp.Opts.SecretStore.GetGroupForAccount() require.NoError(t, err) publicKeyBytes, err := accMemberDevice.Member().Raw() require.NoError(t, err) return publicKeyBytes } func getAccountB64PubKey(t *testing.T, tp *weshnet.TestingProtocol) string { t.Helper() tpPK := getAccountPubKey(t, tp) return base64.StdEncoding.EncodeToString(tpPK) } ================================================ FILE: service.go ================================================ package weshnet import ( "context" "encoding/hex" "fmt" mrand "math/rand" "path/filepath" "sync" "time" "unsafe" "github.com/dgraph-io/badger/v2/options" ds "github.com/ipfs/go-datastore" ds_sync "github.com/ipfs/go-datastore/sync" badger "github.com/ipfs/go-ds-badger2" coreiface "github.com/ipfs/kubo/core/coreiface" pubsub "github.com/libp2p/go-libp2p-pubsub" "github.com/libp2p/go-libp2p/core/crypto" "github.com/libp2p/go-libp2p/core/event" "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/network" "github.com/libp2p/go-libp2p/core/peerstore" backoff "github.com/libp2p/go-libp2p/p2p/discovery/backoff" "github.com/libp2p/go-libp2p/p2p/host/eventbus" "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" "go.uber.org/multierr" "go.uber.org/zap" "moul.io/srand" "berty.tech/go-orbit-db/baseorbitdb" "berty.tech/go-orbit-db/iface" "berty.tech/go-orbit-db/pubsub/directchannel" "berty.tech/go-orbit-db/pubsub/pubsubraw" "berty.tech/weshnet/v2/internal/bertyversion" "berty.tech/weshnet/v2/internal/datastoreutil" "berty.tech/weshnet/v2/pkg/bertyvcissuer" "berty.tech/weshnet/v2/pkg/errcode" "berty.tech/weshnet/v2/pkg/ipfsutil" ipfs_mobile "berty.tech/weshnet/v2/pkg/ipfsutil/mobile" "berty.tech/weshnet/v2/pkg/protocoltypes" "berty.tech/weshnet/v2/pkg/rendezvous" "berty.tech/weshnet/v2/pkg/secretstore" tinder "berty.tech/weshnet/v2/pkg/tinder" "berty.tech/weshnet/v2/pkg/tyber" ) var _ Service = (*service)(nil) // Service is the main Berty Protocol interface type Service interface { protocoltypes.ProtocolServiceServer Close() error Status() Status IpfsCoreAPI() coreiface.CoreAPI } type service struct { // variables ctx context.Context ctxCancel context.CancelFunc logger *zap.Logger ipfsCoreAPI ipfsutil.ExtendedCoreAPI odb *WeshOrbitDB accountGroupCtx *GroupContext openedGroups map[string]*GroupContext lock sync.RWMutex close func() error startedAt time.Time host host.Host grpcInsecure bool refreshprocess map[string]context.CancelFunc muRefreshprocess sync.RWMutex swiper *Swiper peerStatusManager *ConnectednessManager accountEventBus event.Bus contactRequestsManager *contactRequestsManager vcClient *bertyvcissuer.Client secretStore secretstore.SecretStore protocoltypes.UnimplementedProtocolServiceServer } // Opts contains optional configuration flags for building a new Client type Opts struct { Logger *zap.Logger IpfsCoreAPI ipfsutil.ExtendedCoreAPI DatastoreDir string RootDatastore ds.Batching OrbitDB *WeshOrbitDB TinderService *tinder.Service Host host.Host PubSub *pubsub.PubSub GRPCInsecureMode bool LocalOnly bool close func() error SecretStore secretstore.SecretStore PrometheusRegister prometheus.Registerer // P2PStaticRelays is only used if IpfsCoreAPI is nil P2PStaticRelays []string // P2PRdvpMaddrs is only used if TinderService is nil P2PRdvpMaddrs []string // These are used if OrbitDB is nil. GroupMetadataStoreType string GroupMessageStoreType string } func (opts *Opts) applyPushDefaults() { if opts.Logger == nil { opts.Logger = zap.NewNop() } if opts.PrometheusRegister == nil { opts.PrometheusRegister = prometheus.DefaultRegisterer } } func (opts *Opts) applyDefaultsGetDatastore() error { if opts.RootDatastore == nil { if opts.DatastoreDir == "" || opts.DatastoreDir == InMemoryDirectory { opts.RootDatastore = ds_sync.MutexWrap(ds.NewMapDatastore()) } else { bopts := badger.DefaultOptions bopts.ValueLogLoadingMode = options.FileIO ds, err := badger.NewDatastore(opts.DatastoreDir, &bopts) if err != nil { return fmt.Errorf("unable to init badger datastore: %w", err) } opts.RootDatastore = ds oldClose := opts.close opts.close = func() error { var err error if oldClose != nil { err = oldClose() } if dserr := ds.Close(); dserr != nil { err = multierr.Append(err, fmt.Errorf("unable to close datastore: %w", dserr)) } return err } } } return nil } func (opts *Opts) applyDefaults(ctx context.Context) error { if opts.Logger == nil { opts.Logger = zap.NewNop() } rng := mrand.New(mrand.NewSource(srand.MustSecure())) // nolint:gosec // we need to use math/rand here, but it is seeded from crypto/rand if err := opts.applyDefaultsGetDatastore(); err != nil { return err } opts.applyPushDefaults() if opts.SecretStore == nil { secretStore, err := secretstore.NewSecretStore(opts.RootDatastore, &secretstore.NewSecretStoreOptions{ Logger: opts.Logger, }) if err != nil { return errcode.ErrCode_ErrInternal.Wrap(err) } opts.SecretStore = secretStore } if opts.P2PRdvpMaddrs == nil { opts.P2PRdvpMaddrs = []string{ipfsutil.DefaultP2PRdvpMaddr} } var mnode *ipfs_mobile.IpfsMobile if opts.IpfsCoreAPI == nil { dsync := opts.RootDatastore if dsync == nil { dsync = ds_sync.MutexWrap(ds.NewMapDatastore()) } repo, err := ipfsutil.CreateMockedRepo(dsync) if err != nil { return err } mrepo := ipfs_mobile.NewRepoMobile(opts.DatastoreDir, repo) // NewIPFSMobile will apply defaults for P2PStaticRelays mnode, err = ipfsutil.NewIPFSMobile(ctx, mrepo, &ipfsutil.MobileOptions{ Logger: opts.Logger, P2PStaticRelays: opts.P2PStaticRelays, PeerStorePeers: opts.P2PRdvpMaddrs, }) if err != nil { return err } opts.IpfsCoreAPI, err = ipfsutil.NewExtendedCoreAPIFromNode(mnode.IpfsNode) if err != nil { return err } opts.Host = mnode.PeerHost() oldClose := opts.close opts.close = func() error { if oldClose != nil { _ = oldClose() } return mnode.Close() } } if opts.Host == nil { opts.Host = opts.IpfsCoreAPI } // setup default tinder service if opts.TinderService == nil { drivers := []tinder.IDriver{} // setup loac disc localdisc, err := tinder.NewLocalDiscovery(opts.Logger, opts.Host, rng) if err != nil { return fmt.Errorf("unable to setup tinder localdiscovery: %w", err) } drivers = append(drivers, localdisc) // rdvp driver. Imitate berty configIPFSRouting // https://github.com/berty/berty/blob/5a8b9cb8524c1287ab2533a9e186ac8bde7f2b57/go/internal/initutil/ipfs.go#L684 rdvpeers, err := ipfsutil.ParseAndResolveMaddrs(ctx, opts.Logger, opts.P2PRdvpMaddrs) if err != nil { return fmt.Errorf("unable to resolve maddrs: %w", err) } addrsFactory := tinder.PublicAddrsOnlyFactory if len(rdvpeers) > 0 { for _, peer := range rdvpeers { opts.Host.Peerstore().AddAddrs(peer.ID, peer.Addrs, peerstore.PermanentAddrTTL) emitterclient := rendezvous.NewEmitterClient(&rendezvous.EmitterClientOptions{ Logger: opts.Logger, }) // mqttclient := rendezvous.NewMQTTClient(logger, baseopts) udisc := tinder.NewRendezvousDiscovery(opts.Logger, opts.Host, peer.ID, addrsFactory, rng, emitterclient) drivers = append(drivers, udisc) } } if mnode != nil { dhtdisc := tinder.NewRoutingDiscoveryDriver("dht", mnode.DHT) drivers = append(drivers, dhtdisc) } opts.TinderService, err = tinder.NewService(opts.Host, opts.Logger, drivers...) if err != nil { return fmt.Errorf("unable to setup tinder service: %w", err) } } if opts.PubSub == nil { var err error popts := []pubsub.Option{ pubsub.WithMessageSigning(true), pubsub.WithPeerExchange(true), } backoffstrat := backoff.NewExponentialBackoff( time.Second*10, time.Hour, backoff.FullJitter, time.Second, 10.0, 0, rng) cacheSize := 100 dialTimeout := time.Second * 20 backoffconnector := func(host host.Host) (*backoff.BackoffConnector, error) { return backoff.NewBackoffConnector(host, cacheSize, dialTimeout, backoffstrat) } adaptater := tinder.NewDiscoveryAdaptater(opts.Logger.Named("disc"), opts.TinderService) popts = append(popts, pubsub.WithDiscovery(adaptater, pubsub.WithDiscoverConnector(backoffconnector))) // pubsub.DiscoveryPollInterval = m.Node.Protocol.PollInterval ps, err := pubsub.NewGossipSub(ctx, opts.Host, popts...) if err != nil { return fmt.Errorf("unable to init gossipsub: %w", err) } // @NOTE(gfanton): we need to force cast here until our fix is push // upstream on the original go-libp2p-pubsub // see: https://github.com/gfanton/go-libp2p-pubsub/commit/8f4fd394f8dfcb3a5eb724a03f9e4e1e33194cbd opts.PubSub = (*pubsub.PubSub)(unsafe.Pointer(ps)) } if opts.OrbitDB == nil { orbitDirectory := InMemoryDirectory if opts.DatastoreDir != InMemoryDirectory { orbitDirectory = filepath.Join(opts.DatastoreDir, NamespaceOrbitDBDirectory) } pubsub := pubsubraw.NewPubSub(opts.PubSub, opts.Host.ID(), opts.Logger, nil) odbOpts := &NewOrbitDBOptions{ NewOrbitDBOptions: baseorbitdb.NewOrbitDBOptions{ Directory: &orbitDirectory, PubSub: pubsub, Logger: opts.Logger, }, PrometheusRegister: opts.PrometheusRegister, Datastore: datastoreutil.NewNamespacedDatastore(opts.RootDatastore, ds.NewKey(NamespaceOrbitDBDatastore)), SecretStore: opts.SecretStore, GroupMetadataStoreType: opts.GroupMetadataStoreType, GroupMessageStoreType: opts.GroupMessageStoreType, } if opts.Host != nil { odbOpts.DirectChannelFactory = directchannel.InitDirectChannelFactory(opts.Logger, opts.Host) } odb, err := NewWeshOrbitDB(ctx, opts.IpfsCoreAPI, odbOpts) if err != nil { return err } oldClose := opts.close opts.close = func() error { if oldClose != nil { _ = oldClose() } return odb.Close() } opts.OrbitDB = odb } return nil } // NewService initializes a new Service using the opts. // If opts.RootDatastore is nil and opts.DatastoreDir is "" or InMemoryDirectory, then set // opts.RootDatastore to an in-memory data store. Otherwise, if opts.RootDatastore is nil then set // opts.RootDatastore to a persistent data store at opts.DatastoreDir . func NewService(opts Opts) (_ Service, err error) { ctx, cancel := context.WithCancel(context.Background()) if err := opts.applyDefaults(ctx); err != nil { cancel() return nil, errcode.ErrCode_TODO.Wrap(err) } opts.Logger = opts.Logger.Named("pt") ctx, _, endSection := tyber.Section(tyber.ContextWithoutTraceID(ctx), opts.Logger, fmt.Sprintf("Initializing ProtocolService version %s", bertyversion.Version)) defer func() { endSection(err, "") }() accountEventBus := eventbus.NewBus( eventbus.WithMetricsTracer(eventbus.NewMetricsTracer(eventbus.WithRegisterer(opts.PrometheusRegister)))) dbOpts := &iface.CreateDBOptions{ EventBus: accountEventBus, LocalOnly: &opts.LocalOnly, } accountGroupCtx, err := opts.OrbitDB.openAccountGroup(ctx, dbOpts, opts.IpfsCoreAPI) if err != nil { cancel() return nil, errcode.ErrCode_TODO.Wrap(err) } opts.Logger.Debug("Opened account group", tyber.FormatStepLogFields(ctx, []tyber.Detail{{Name: "AccountGroup", Description: accountGroupCtx.group.String()}})...) var contactRequestsManager *contactRequestsManager var swiper *Swiper if opts.TinderService != nil { swiper = NewSwiper(opts.Logger, opts.TinderService, opts.OrbitDB.rotationInterval) opts.Logger.Debug("Tinder swiper is enabled", tyber.FormatStepLogFields(ctx, []tyber.Detail{})...) if contactRequestsManager, err = newContactRequestsManager(swiper, accountGroupCtx.metadataStore, opts.IpfsCoreAPI, opts.Logger); err != nil { cancel() return nil, errcode.ErrCode_TODO.Wrap(err) } } else { opts.Logger.Warn("No tinder driver provided, incoming and outgoing contact requests won't be enabled", tyber.FormatStepLogFields(ctx, []tyber.Detail{})...) } if err := opts.SecretStore.PutGroup(ctx, accountGroupCtx.Group()); err != nil { cancel() return nil, errcode.ErrCode_ErrInternal.Wrap(fmt.Errorf("unable to add account group to group datastore, err: %w", err)) } s := &service{ ctx: ctx, ctxCancel: cancel, host: opts.Host, ipfsCoreAPI: opts.IpfsCoreAPI, logger: opts.Logger, odb: opts.OrbitDB, close: opts.close, accountGroupCtx: accountGroupCtx, swiper: swiper, startedAt: time.Now(), openedGroups: map[string]*GroupContext{ string(accountGroupCtx.Group().PublicKey): accountGroupCtx, }, secretStore: opts.SecretStore, grpcInsecure: opts.GRPCInsecureMode, refreshprocess: make(map[string]context.CancelFunc), peerStatusManager: NewConnectednessManager(), accountEventBus: accountEventBus, contactRequestsManager: contactRequestsManager, } s.startGroupDeviceMonitor() return s, nil } func (s *service) IpfsCoreAPI() coreiface.CoreAPI { return s.ipfsCoreAPI } func (s *service) Close() error { endSection := tyber.SimpleSection(tyber.ContextWithoutTraceID(s.ctx), s.logger, "Closing ProtocolService") var err error pks := []crypto.PubKey{} // gather public keys s.lock.Lock() if s.contactRequestsManager != nil { s.contactRequestsManager.close() s.contactRequestsManager = nil } for _, gc := range s.openedGroups { pk, subErr := gc.group.GetPubKey() if subErr != nil { err = multierr.Append(err, subErr) continue } pks = append(pks, pk) } s.lock.Unlock() // deactivate all groups for _, pk := range pks { derr := s.deactivateGroup(pk) if derr != nil { err = multierr.Append(derr, derr) } } err = multierr.Append(err, s.odb.Close()) if s.close != nil { err = multierr.Append(err, s.close()) } endSection(err) s.ctxCancel() return err } func (s *service) startGroupDeviceMonitor() { if s.host == nil { return } // monitor exchange heads events subHead, err := s.odb.EventBus().Subscribe(new(baseorbitdb.EventExchangeHeads), eventbus.Name("weshnet/service/monitor-exchange-heads")) if err != nil { s.logger.Error("startGroupDeviceMonitor", zap.Error(errors.Wrap(err, "unable to subscribe odb event"))) return } // monitor peer connectednesschanged subPeer, err := s.host.EventBus().Subscribe(new(event.EvtPeerConnectednessChanged), eventbus.Name("weshnet/service/monitor-peer-connectedness")) if err != nil { s.logger.Error("startGroupDeviceMonitor", zap.Error(errors.Wrap(err, "unable to subscribe odb event"))) subHead.Close() return } go func() { defer subHead.Close() defer subPeer.Close() for { var evt any select { case evt = <-subHead.Out(): case evt = <-subPeer.Out(): case <-s.ctx.Done(): return } switch e := evt.(type) { case event.EvtPeerConnectednessChanged: switch e.Connectedness { case network.Connected: s.peerStatusManager.UpdateState(e.Peer, ConnectednessTypeConnected) case network.NotConnected: s.peerStatusManager.UpdateState(e.Peer, ConnectednessTypeDisconnected) } case baseorbitdb.EventExchangeHeads: if dpk, ok := s.odb.GetDevicePKForPeerID(e.Peer); ok { gkey := hex.EncodeToString(dpk.Group.PublicKey) s.peerStatusManager.AssociatePeer(gkey, e.Peer) } } } }() // get status of peers in the peerstore peers := s.host.Peerstore().Peers() for _, peer := range peers { // if we got some connected peer check their status if s.host.Network().Connectedness(peer) == network.Connected { s.peerStatusManager.UpdateState(peer, ConnectednessTypeConnected) } // if we already have some head exchange with this peer, associate it if dpk, ok := s.odb.GetDevicePKForPeerID(peer); ok { gkey := hex.EncodeToString(dpk.Group.PublicKey) s.peerStatusManager.AssociatePeer(gkey, peer) } } } // Status contains results of status checks type Status struct { DB error Protocol error } func (s *service) Status() Status { return Status{ Protocol: nil, } } ================================================ FILE: service_client.go ================================================ package weshnet import ( "context" "fmt" "io" "os" "time" "go.uber.org/zap" "google.golang.org/grpc" "berty.tech/weshnet/v2/pkg/grpcutil" "berty.tech/weshnet/v2/pkg/ipfsutil" ipfs_mobile "berty.tech/weshnet/v2/pkg/ipfsutil/mobile" "berty.tech/weshnet/v2/pkg/logutil" "berty.tech/weshnet/v2/pkg/protocoltypes" ) const ( defaultLoggingFiltersKey = ":default:" defaultLoggingFiltersValue = "info+:bty.* error+:*,-ipfs*,-*.tyber" ) type ServiceClient interface { protocoltypes.ProtocolServiceClient io.Closer } type ServiceOption func(*Opts) error // WithLogger sets the given logger var WithLogger = func(l *zap.Logger) ServiceOption { return func(s *Opts) error { s.Logger = l return nil } } // WithP2PStaticRelays sets the given P2P static relays var WithP2PStaticRelays = func(p []string) ServiceOption { return func(s *Opts) error { s.P2PStaticRelays = p return nil } } // WithP2PRdvpMaddrs sets the given P2P rendezvous point addresses var WithP2PRdvpMaddrs = func(p []string) ServiceOption { return func(s *Opts) error { s.P2PRdvpMaddrs = p return nil } } // NewServiceClient initializes a new ServiceClient using the opts. // If opts.RootDatastore is nil and opts.DatastoreDir is "" or InMemoryDirectory, then set // opts.RootDatastore to an in-memory data store. Otherwise, if opts.RootDatastore is nil then set // opts.RootDatastore to a persistent data store at opts.DatastoreDir . func NewServiceClient(opts Opts) (ServiceClient, error) { var err error var cleanupLogger func() if opts.Logger == nil { if opts.Logger, cleanupLogger, err = setupDefaultLogger(); err != nil { return nil, fmt.Errorf("unable to setup logger: %w", err) } } svc, err := NewService(opts) if err != nil { return nil, err } s := grpc.NewServer() ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) defer cancel() c, err := NewClientFromService(ctx, s, svc) if err != nil { return nil, fmt.Errorf("unable to create client from server: %w", err) } return &serviceClient{ ServiceClient: c, server: s, service: svc, cleanup: cleanupLogger, }, nil } // NewInMemoryServiceClient creates a new in-memory Wesh protocol service and returns a gRPC // ServiceClient which uses a direct in-memory connection. When finished, you must call Close(). // This creates a new Wesh account where the key store is in memory. (If you don't // export the data then it is lost when you call Close(). ) The IPFS node, cached data, // and configuration are also in memory. func NewInMemoryServiceClient(options ...ServiceOption) (ServiceClient, error) { var opts Opts for _, opt := range options { if err := opt(&opts); err != nil { return nil, err } } opts.DatastoreDir = InMemoryDirectory return NewServiceClient(opts) } // NewPersistentServiceClient creates a Wesh protocol service using persistent storage files in the // directory given by the directory path. If the directory doesn't exist, this creates it with files // of a new Wesh account and peer identity. (If the directory doesn't exist, this will create it only // if the parent directory exists. Otherwise you must first create the parent directories.) However, // if the persistent storage files already exist, then this opens them to use the existing Wesh // account and peer identity. This returns a gRPC ServiceClient which uses a direct in-memory // connection. When finished, you must call Close(). func NewPersistentServiceClient(path string, options ...ServiceOption) (ServiceClient, error) { var opts Opts for _, opt := range options { if err := opt(&opts); err != nil { return nil, err } } opts.DatastoreDir = path repo, err := ipfsutil.LoadRepoFromPath(path) if err != nil { return nil, err } var cleanupLogger func() if opts.Logger == nil { if opts.Logger, cleanupLogger, err = setupDefaultLogger(); err != nil { return nil, fmt.Errorf("unable to setup logger: %w", err) } } mrepo := ipfs_mobile.NewRepoMobile(path, repo) mnode, err := ipfsutil.NewIPFSMobile(context.TODO(), mrepo, &ipfsutil.MobileOptions{ Logger: opts.Logger, P2PStaticRelays: opts.P2PStaticRelays, PeerStorePeers: opts.P2PRdvpMaddrs, }) if err != nil { return nil, err } opts.IpfsCoreAPI, err = ipfsutil.NewExtendedCoreAPIFromNode(mnode.IpfsNode) if err != nil { return nil, err } cl, err := NewServiceClient(opts) if err != nil { return nil, err } return &persistentServiceClient{ ServiceClient: cl, cleanup: cleanupLogger, }, nil } const ClientBufferSize = 4 * 1024 * 1024 type serviceClient struct { ServiceClient // inehrit from client service Service server *grpc.Server cleanup func() } type persistentServiceClient struct { ServiceClient cleanup func() } func (p *persistentServiceClient) Close() error { err := p.ServiceClient.Close() if p.cleanup != nil { p.cleanup() } return err } func (c *serviceClient) Close() (err error) { c.server.GracefulStop() // gracefully stop grpc server _ = c.ServiceClient.Close() // close client and discard error err = c.service.Close() if c.cleanup != nil { c.cleanup() } return // return real service error } type client struct { protocoltypes.ProtocolServiceClient l *grpcutil.BufListener cc *grpc.ClientConn } func (c *client) Close() error { err := c.cc.Close() _ = c.l.Close() return err } func NewClientFromService(ctx context.Context, s *grpc.Server, svc Service, opts ...grpc.DialOption) (ServiceClient, error) { bl := grpcutil.NewBufListener(ClientBufferSize) cc, err := bl.NewClientConn(ctx, opts...) if err != nil { return nil, err } protocoltypes.RegisterProtocolServiceServer(s, svc) go func() { // we dont need to log the error _ = s.Serve(bl) }() return &client{ ProtocolServiceClient: protocoltypes.NewProtocolServiceClient(cc), cc: cc, l: bl, }, nil } func setupDefaultLogger() (logger *zap.Logger, cleanup func(), err error) { // setup log from env if logfilter := os.Getenv("WESHNET_LOG_FILTER"); logfilter != "" { if logfilter == defaultLoggingFiltersKey { logfilter = defaultLoggingFiltersValue } s := logutil.NewStdStream(logfilter, "color", os.Stderr.Name()) return logutil.NewLogger(s) } return zap.NewNop(), func() {}, nil } ================================================ FILE: service_group.go ================================================ package weshnet import ( "context" "fmt" "github.com/libp2p/go-libp2p/core/crypto" "go.uber.org/zap" "berty.tech/go-orbit-db/iface" "berty.tech/weshnet/v2/pkg/errcode" "berty.tech/weshnet/v2/pkg/protocoltypes" "berty.tech/weshnet/v2/pkg/secretstore" ) func (s *service) getContactGroup(key crypto.PubKey) (*protocoltypes.Group, error) { group, err := s.secretStore.GetGroupForContact(key) if err != nil { return nil, errcode.ErrCode_ErrOrbitDBOpen.Wrap(err) } return group, nil } func (s *service) getGroupForPK(ctx context.Context, pk crypto.PubKey) (*protocoltypes.Group, error) { group, err := s.secretStore.FetchGroupByPublicKey(ctx, pk) if err == nil { return group, nil } else if !errcode.Is(err, errcode.ErrCode_ErrMissingMapKey) { return nil, errcode.ErrCode_ErrInternal.Wrap(err) } accountGroup := s.getAccountGroup() if accountGroup == nil { return nil, errcode.ErrCode_ErrGroupMissing } if err = reindexGroupDatastore(ctx, s.secretStore, accountGroup.metadataStore); err != nil { return nil, errcode.ErrCode_TODO.Wrap(err) } group, err = s.secretStore.FetchGroupByPublicKey(ctx, pk) if err == nil { return group, nil } else if errcode.Is(err, errcode.ErrCode_ErrMissingMapKey) { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("unknown group specified")) } return nil, errcode.ErrCode_ErrInternal.Wrap(err) } func (s *service) deactivateGroup(pk crypto.PubKey) error { id, err := pk.Raw() if err != nil { return errcode.ErrCode_ErrSerialization.Wrap(err) } cg, err := s.GetContextGroupForID(id) if err != nil || cg == nil { // @FIXME(gfanton): should return an error code return nil } s.lock.Lock() defer s.lock.Unlock() err = cg.Close() if err != nil { s.logger.Error("unable to close group context", zap.Error(err)) } delete(s.openedGroups, string(id)) if cg.group.GroupType == protocoltypes.GroupType_GroupTypeAccount { s.accountGroupCtx = nil } return nil } func (s *service) activateGroup(ctx context.Context, pk crypto.PubKey, localOnly bool) error { id, err := pk.Raw() if err != nil { return errcode.ErrCode_ErrSerialization.Wrap(err) } _, err = s.GetContextGroupForID(id) if err != nil && err != errcode.ErrCode_ErrGroupUnknown { return err } g, err := s.getGroupForPK(ctx, pk) if err != nil { return errcode.ErrCode_ErrInternal.Wrap(err) } s.lock.Lock() defer s.lock.Unlock() // @WIP(gfanton): do we need to use contactPK var contactPK crypto.PubKey switch g.GroupType { case protocoltypes.GroupType_GroupTypeMultiMember: // nothing to get here, simply continue, open and activate the group case protocoltypes.GroupType_GroupTypeContact: if s.accountGroupCtx == nil { return errcode.ErrCode_ErrGroupActivate.Wrap(fmt.Errorf("accountGroupCtx is deactivated")) } contact := s.accountGroupCtx.metadataStore.GetContactFromGroupPK(id) if contact != nil { contactPK, err = contact.GetPubKey() if err != nil { return errcode.ErrCode_TODO.Wrap(err) } } case protocoltypes.GroupType_GroupTypeAccount: localOnly = true if s.accountGroupCtx, err = s.odb.openAccountGroup(ctx, &iface.CreateDBOptions{EventBus: s.accountEventBus, LocalOnly: &localOnly}, s.ipfsCoreAPI); err != nil { return err } s.openedGroups[string(id)] = s.accountGroupCtx // reinitialize contactRequestsManager if s.contactRequestsManager != nil { s.contactRequestsManager.close() if s.contactRequestsManager, err = newContactRequestsManager(s.swiper, s.accountGroupCtx.metadataStore, s.ipfsCoreAPI, s.logger); err != nil { return errcode.ErrCode_TODO.Wrap(err) } } return nil default: return errcode.ErrCode_ErrInternal.Wrap(fmt.Errorf("unknown group type")) } dbOpts := &iface.CreateDBOptions{LocalOnly: &localOnly} gc, err := s.odb.OpenGroup(ctx, g, dbOpts) if err != nil { return errcode.ErrCode_ErrGroupOpen.Wrap(err) } if err = gc.ActivateGroupContext(contactPK); err != nil { gc.Close() return errcode.ErrCode_ErrGroupActivate.Wrap(err) } s.openedGroups[string(id)] = gc gc.TagGroupContextPeers(s.ipfsCoreAPI, 42) return nil } func (s *service) GetContextGroupForID(id []byte) (*GroupContext, error) { if len(id) == 0 { return nil, errcode.ErrCode_ErrInternal.Wrap(fmt.Errorf("no group id provided")) } s.lock.RLock() defer s.lock.RUnlock() cg, ok := s.openedGroups[string(id)] if ok { return cg, nil } return nil, errcode.ErrCode_ErrGroupUnknown } func reindexGroupDatastore(ctx context.Context, secretStore secretstore.SecretStore, m *MetadataStore) error { if secretStore == nil { return errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("missing device keystore")) } for _, g := range m.ListMultiMemberGroups() { if err := secretStore.PutGroup(ctx, g); err != nil { return errcode.ErrCode_ErrInternal.Wrap(err) } } for _, contact := range m.ListContactsByStatus( protocoltypes.ContactState_ContactStateToRequest, protocoltypes.ContactState_ContactStateReceived, protocoltypes.ContactState_ContactStateAdded, protocoltypes.ContactState_ContactStateRemoved, protocoltypes.ContactState_ContactStateDiscarded, protocoltypes.ContactState_ContactStateBlocked, ) { cPK, err := contact.GetPubKey() if err != nil { return errcode.ErrCode_TODO.Wrap(err) } group, err := secretStore.GetGroupForContact(cPK) if err != nil { return errcode.ErrCode_ErrInternal.Wrap(err) } if err := secretStore.PutGroup(ctx, group); err != nil { return errcode.ErrCode_ErrInternal.Wrap(err) } } return nil } func (s *service) getAccountGroup() *GroupContext { s.lock.Lock() defer s.lock.Unlock() return s.accountGroupCtx } ================================================ FILE: store_message.go ================================================ package weshnet import ( "bytes" "context" "encoding/base64" "fmt" "sync" "github.com/ipfs/go-cid" coreiface "github.com/ipfs/kubo/core/coreiface" "github.com/libp2p/go-libp2p/core/crypto" "github.com/libp2p/go-libp2p/core/event" "github.com/libp2p/go-libp2p/p2p/host/eventbus" "go.uber.org/zap" "google.golang.org/protobuf/proto" ipfslog "berty.tech/go-ipfs-log" "berty.tech/go-ipfs-log/identityprovider" ipliface "berty.tech/go-ipfs-log/iface" "berty.tech/go-orbit-db/address" "berty.tech/go-orbit-db/iface" "berty.tech/go-orbit-db/stores" "berty.tech/go-orbit-db/stores/basestore" "berty.tech/go-orbit-db/stores/operation" "berty.tech/weshnet/v2/pkg/errcode" "berty.tech/weshnet/v2/pkg/logutil" "berty.tech/weshnet/v2/pkg/protocoltypes" "berty.tech/weshnet/v2/pkg/secretstore" "berty.tech/weshnet/v2/pkg/tyber" ) // FIXME: replace cache by a circular buffer to avoid an attack by RAM saturation type MessageStore struct { basestore.BaseStore eventBus event.Bus emitters struct { groupMessage event.Emitter groupCacheMessage event.Emitter } secretStore secretstore.SecretStore currentDevicePublicKey crypto.PubKey currentDevicePublicKeyRaw []byte group *protocoltypes.Group groupPublicKey crypto.PubKey logger *zap.Logger deviceCaches map[string]*groupCache muDeviceCaches sync.RWMutex messagesQueue *simpleMessageQueue ctx context.Context cancel context.CancelFunc } func (m *MessageStore) setLogger(l *zap.Logger) { if l == nil { return } m.logger = l.With(logutil.PrivateString("group-id", fmt.Sprintf("%.6s", base64.StdEncoding.EncodeToString(m.group.PublicKey)))) } func (m *MessageStore) openMessage(ctx context.Context, e ipfslog.Entry) (*protocoltypes.GroupMessageEvent, error) { if e == nil { return nil, errcode.ErrCode_ErrInvalidInput } op, err := operation.ParseOperation(e) if err != nil { m.logger.Error("unable to parse operation", zap.Error(err)) return nil, err } env, headers, err := m.secretStore.OpenEnvelopeHeaders(op.GetValue(), m.group) if err != nil { return nil, errcode.ErrCode_ErrCryptoDecrypt.Wrap(err) } devicePublicKey, err := crypto.UnmarshalEd25519PublicKey(headers.DevicePk) if err != nil { return nil, errcode.ErrCode_ErrDeserialization.Wrap(err) } if !m.secretStore.IsChainKeyKnownForDevice(ctx, m.groupPublicKey, devicePublicKey) { if err := m.addToMessageQueue(ctx, e); err != nil { m.logger.Error("unable to add message to cache", zap.Error(err)) } return nil, fmt.Errorf("no secret for device") } return m.processMessage(ctx, &messageItem{ op: op, env: env, headers: headers, hash: e.GetHash(), }) } type groupCache struct { self, hasKnownChainKey bool locker sync.Locker queue *priorityMessageQueue } func (m *MessageStore) CacheSizeForDevicePK(devicePK []byte) (size int, ok bool) { m.muDeviceCaches.RLock() var device *groupCache if device, ok = m.deviceCaches[string(devicePK)]; ok { size = device.queue.Size() } m.muDeviceCaches.RUnlock() return } func (m *MessageStore) ProcessMessageQueueForDevicePK(ctx context.Context, devicePK []byte) { m.muDeviceCaches.Lock() if device, ok := m.deviceCaches[string(devicePK)]; ok { devicePublicKey, errDevice := crypto.UnmarshalEd25519PublicKey(devicePK) if errDevice != nil { m.logger.Error("unable to process message, unmarshal of device pk failed", logutil.PrivateBinary("devicepk", devicePK)) } else if device.hasKnownChainKey = m.secretStore.IsChainKeyKnownForDevice(ctx, m.groupPublicKey, devicePublicKey); !device.hasKnownChainKey { m.logger.Error("unable to process message, no secret found for device pk", logutil.PrivateBinary("devicepk", devicePK)) } else if next := device.queue.Next(); next != nil { // let's try processing one message from the queue. // if it succeeds, the whole queue should be added for processing. m.messagesQueue.Add(next) } } m.muDeviceCaches.Unlock() } func (m *MessageStore) processMessage(ctx context.Context, message *messageItem) (*protocoltypes.GroupMessageEvent, error) { // process message msg, err := m.secretStore.OpenEnvelopePayload(ctx, message.env, message.headers, m.groupPublicKey, m.currentDevicePublicKey, message.hash) if err != nil { return nil, fmt.Errorf("unable to open the envelope: %w", err) } err = m.secretStore.UpdateOutOfStoreGroupReferences(ctx, message.headers.DevicePk, message.headers.Counter, m.group) if err != nil { m.logger.Error("unable to update push group references", zap.Error(err)) } entry := message.op.GetEntry() eventContext := newEventContext(entry.GetHash(), entry.GetNext(), m.group) return &protocoltypes.GroupMessageEvent{ EventContext: eventContext, Headers: message.headers, Message: msg.GetPlaintext(), }, nil } func (m *MessageStore) processMessageLoop(ctx context.Context, tracer *messageMetricsTracer) { for { // wait for next message message, ok := m.messagesQueue.WaitForItem(ctx) if !ok { // context expired, return return } // get or create a device cache for the device from which we received the message. device, hasKnownChainKey := m.getOrCreateDeviceCache(ctx, message, tracer) if device == nil { // unknown device, lets keep moving continue } else if !hasKnownChainKey { // we dont know the chain key yet, add message to the device cache device.queue.Add(message) _ = m.emitters.groupCacheMessage.Emit(*message) continue } // actually process the message evt, err := m.processMessage(ctx, message) if err != nil { m.logger.Error("unable to process message", zap.Error(err)) // if we got any error here, put (back) the message into the device queue // for ex: `too many open files` error device.queue.Add(message) _ = m.emitters.groupCacheMessage.Emit(*message) continue } // if we get here we probably can process other messages (if any) in the device queue m.processDeviceMessagesInQueue(device) // emit new message event if err := m.emitters.groupMessage.Emit(evt); err != nil { m.logger.Warn("unable to emit group message event", zap.Error(err)) } } } func (m *MessageStore) getOrCreateDeviceCache(ctx context.Context, message *messageItem, tracer *messageMetricsTracer) (device *groupCache, hasKnownChainKey bool) { devicePublicKeyString := string(message.headers.DevicePk) m.muDeviceCaches.Lock() defer m.muDeviceCaches.Unlock() device, ok := m.deviceCaches[devicePublicKeyString] if !ok { devicePublicKey, err := crypto.UnmarshalEd25519PublicKey(message.headers.DevicePk) if err != nil { m.logger.Error("unable to process message, unmarshal of device pk failed", logutil.PrivateBinary("devicepk", message.headers.DevicePk)) return nil, false } hasSecret := m.secretStore.IsChainKeyKnownForDevice(ctx, m.groupPublicKey, devicePublicKey) device = &groupCache{ self: bytes.Equal(m.currentDevicePublicKeyRaw, message.headers.DevicePk), queue: newPriorityMessageQueue("undecrypted", tracer), locker: &sync.RWMutex{}, hasKnownChainKey: hasSecret, } m.deviceCaches[devicePublicKeyString] = device } return device, device.hasKnownChainKey } // process the whole device queue (if any) into to the message queue func (m *MessageStore) processDeviceMessagesInQueue(device *groupCache) { _ = device.queue.NextAll(func(next *messageItem) error { m.messagesQueue.Add(next) return nil }) } func (m *MessageStore) addToMessageQueue(_ context.Context, e ipfslog.Entry) error { if e == nil { return errcode.ErrCode_ErrInvalidInput } op, err := operation.ParseOperation(e) if err != nil { return err } env, headers, err := m.secretStore.OpenEnvelopeHeaders(op.GetValue(), m.group) if err != nil { return errcode.ErrCode_ErrCryptoDecrypt.Wrap(err) } msg := &messageItem{ hash: e.GetHash(), env: env, headers: headers, op: op, } m.messagesQueue.Add(msg) return nil } // FIXME: use iterator instead to reduce resource usage (require go-ipfs-log improvements) func (m *MessageStore) ListEvents(ctx context.Context, since, until []byte, reverse bool) (<-chan *protocoltypes.GroupMessageEvent, error) { entries, err := getEntriesInRange(m.OpLog().GetEntries().Reverse().Slice(), since, until) if err != nil { return nil, err } out := make(chan *protocoltypes.GroupMessageEvent) go func() { iterateOverEntries( entries, reverse, func(entry ipliface.IPFSLogEntry) { message, err := m.openMessage(ctx, entry) if err != nil { m.logger.Error("unable to open message", zap.Error(err)) } else { out <- message m.logger.Info("message store - sent 1 event from log history") } }, ) close(out) }() return out, nil } func (m *MessageStore) AddMessage(ctx context.Context, payload []byte) (operation.Operation, error) { ctx, newTrace := tyber.ContextWithTraceID(ctx) if newTrace { m.logger.Debug("Sending message to group "+base64.RawURLEncoding.EncodeToString(m.group.PublicKey), tyber.FormatTraceLogFields(ctx)...) } m.logger.Debug( fmt.Sprintf("Adding message to store with payload of %d bytes", len(payload)), tyber.FormatStepLogFields( ctx, []tyber.Detail{ {Name: "Payload", Description: string(payload)}, }, )..., ) return messageStoreAddMessage(ctx, m.group, m, payload) } func messageStoreAddMessage(ctx context.Context, g *protocoltypes.Group, m *MessageStore, payload []byte) (operation.Operation, error) { msg := &protocoltypes.EncryptedMessage{ Plaintext: payload, ProtocolMetadata: &protocoltypes.ProtocolMetadata{}, } msgBytes, err := proto.Marshal(msg) if err != nil { return nil, errcode.ErrCode_ErrInternal.Wrap(err) } sealedEnvelope, err := m.secretStore.SealEnvelope(ctx, g, msgBytes) if err != nil { return nil, errcode.ErrCode_ErrCryptoEncrypt.Wrap(err) } m.logger.Debug( "Message sealed successfully in secretbox envelope", tyber.FormatStepLogFields( ctx, []tyber.Detail{ {Name: "Cleartext size", Description: fmt.Sprintf("%d bytes", len(msgBytes))}, {Name: "Ciphertext size", Description: fmt.Sprintf("%d bytes", len(sealedEnvelope))}, }, )..., ) op := operation.NewOperation(nil, "ADD", sealedEnvelope) e, err := m.AddOperation(ctx, op, nil) if err != nil { return nil, errcode.ErrCode_ErrOrbitDBAppend.Wrap(err) } m.logger.Debug( "Envelope added to orbit-DB log successfully", tyber.FormatStepLogFields(ctx, []tyber.Detail{})..., ) op, err = operation.ParseOperation(e) if err != nil { return nil, errcode.ErrCode_ErrOrbitDBDeserialization.Wrap(err) } m.logger.Debug( "Operation parsed by orbit-DB successfully", tyber.FormatStepLogFields(ctx, []tyber.Detail{{Name: "CID", Description: op.GetEntry().GetHash().String()}})..., ) return op, nil } func constructorFactoryGroupMessage(s *WeshOrbitDB, logger *zap.Logger) iface.StoreConstructor { metricsTracer := newMessageMetricsTracer(s.prometheusRegister) return func(ipfs coreiface.CoreAPI, identity *identityprovider.Identity, addr address.Address, options *iface.NewStoreOptions) (iface.Store, error) { g, err := s.getGroupFromOptions(options) if err != nil { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(err) } groupPublicKey, err := g.GetPubKey() if err != nil { return nil, errcode.ErrCode_ErrDeserialization.Wrap(err) } if options.EventBus == nil { options.EventBus = s.EventBus() } replication := false store := &MessageStore{ eventBus: options.EventBus, secretStore: s.secretStore, messagesQueue: newMessageQueue("cache", metricsTracer), group: g, groupPublicKey: groupPublicKey, logger: logger, deviceCaches: make(map[string]*groupCache), } if s.replicationMode { replication = true } else { currentMemberDevice, err := s.secretStore.GetOwnMemberDeviceForGroup(g) if err != nil { if errcode.Is(err, errcode.ErrCode_ErrInvalidInput) { replication = true } else { return nil, errcode.ErrCode_ErrOrbitDBInit.Wrap(err) } } else { store.currentDevicePublicKey = currentMemberDevice.Device() store.currentDevicePublicKeyRaw, err = store.currentDevicePublicKey.Raw() if err != nil { return nil, errcode.ErrCode_ErrOrbitDBInit.Wrap(err) } } } store.ctx, store.cancel = context.WithCancel(context.Background()) go func() { store.processMessageLoop(store.ctx, metricsTracer) logger.Debug("store message process loop ended", zap.Error(store.ctx.Err())) }() if store.emitters.groupMessage, err = store.eventBus.Emitter(new(*protocoltypes.GroupMessageEvent)); err != nil { store.cancel() return nil, errcode.ErrCode_ErrOrbitDBInit.Wrap(err) } // for debug/test purpose if store.emitters.groupCacheMessage, err = store.eventBus.Emitter(new(messageItem)); err != nil { store.cancel() return nil, errcode.ErrCode_ErrOrbitDBInit.Wrap(err) } options.Index = basestore.NewNoopIndex if err := store.InitBaseStore(ipfs, identity, addr, options); err != nil { store.cancel() return nil, errcode.ErrCode_ErrOrbitDBInit.Wrap(err) } if replication { return store, nil } chSub, err := store.EventBus().Subscribe([]any{ new(stores.EventWrite), new(stores.EventReplicated), }, eventbus.Name("weshnet/store-message"), eventbus.BufSize(128)) if err != nil { return nil, fmt.Errorf("unable to subscribe to store events") } go func(ctx context.Context) { defer chSub.Close() for { var e any select { case e = <-chSub.Out(): case <-ctx.Done(): return } var entries []ipfslog.Entry switch evt := e.(type) { case stores.EventWrite: entries = []ipfslog.Entry{evt.Entry} case stores.EventReplicated: entries = evt.Entries } for _, entry := range entries { ctx = tyber.ContextWithConstantTraceID(ctx, "msgrcvd-"+entry.GetHash().String()) store.logger.Debug("Received message store event", tyber.FormatTraceLogFields(ctx)...) store.logger.Debug( "Message store event", tyber.FormatStepLogFields(ctx, []tyber.Detail{{Name: "RawEvent", Description: fmt.Sprint(e)}})..., ) if err := store.addToMessageQueue(ctx, entry); err != nil { logger.Error("unable to add message to queue", zap.Error(err)) } } } }(store.ctx) return store, nil } } func (m *MessageStore) GetMessageByCID(c cid.Cid) (operation.Operation, error) { logEntry, ok := m.OpLog().Get(c) if !ok { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("unable to find message entry")) } op, err := operation.ParseOperation(logEntry) if err != nil { return nil, errcode.ErrCode_ErrDeserialization.Wrap(err) } return op, nil } func (m *MessageStore) GetOutOfStoreMessageEnvelope(_ context.Context, c cid.Cid) (*protocoltypes.OutOfStoreMessageEnvelope, error) { op, err := m.GetMessageByCID(c) if err != nil { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(err) } env, headers, err := m.secretStore.OpenEnvelopeHeaders(op.GetValue(), m.group) if err != nil { return nil, errcode.ErrCode_ErrDeserialization.Wrap(err) } sealedMessageEnvelope, err := m.secretStore.SealOutOfStoreMessageEnvelope(c, env, headers, m.group) if err != nil { return nil, errcode.ErrCode_ErrInternal.Wrap(err) } return sealedMessageEnvelope, nil } func (m *MessageStore) Close() error { m.cancel() return m.BaseStore.Close() } ================================================ FILE: store_message_metrics.go ================================================ package weshnet import ( "encoding/hex" "fmt" "github.com/prometheus/client_golang/prometheus" "berty.tech/weshnet/v2/internal/queue" ) const messageMetricNamespace = "bty_store_message" var ( collectorMessageStoreQueueLength = prometheus.NewGaugeVec( prometheus.GaugeOpts{ Namespace: messageMetricNamespace, Name: "message_queue_length", Help: "message queue length", }, []string{"kind", "device_pk"}, ) collectorsMessageStore = []prometheus.Collector{ collectorMessageStoreQueueLength, } ) var _ queue.MetricsTracer[*messageItem] = (*messageMetricsTracer)(nil) type messageMetricsTracer struct { reg prometheus.Registerer } func newMessageMetricsTracer(reg prometheus.Registerer) (mt *messageMetricsTracer) { mt = &messageMetricsTracer{reg: reg} for _, collector := range collectorsMessageStore { if err := reg.Register(collector); err != nil { if _, ok := err.(prometheus.AlreadyRegisteredError); !ok { panic(fmt.Errorf("message metrics errors: %w", err)) } return } } // reg.MustRegister(collectorsMessageStore...) return } func (s *messageMetricsTracer) ItemQueued(name string, m *messageItem) { collectorMessageStoreQueueLength.WithLabelValues( name, hex.EncodeToString(m.headers.DevicePk), ).Inc() } func (s *messageMetricsTracer) ItemPop(name string, m *messageItem) { collectorMessageStoreQueueLength.WithLabelValues( name, hex.EncodeToString(m.headers.DevicePk), ).Dec() } ================================================ FILE: store_message_queue.go ================================================ package weshnet import ( "github.com/ipfs/go-cid" "berty.tech/go-orbit-db/stores/operation" "berty.tech/weshnet/v2/internal/queue" "berty.tech/weshnet/v2/pkg/protocoltypes" ) // An Item is something we manage in a priority queue. type messageItem struct { op operation.Operation env *protocoltypes.MessageEnvelope headers *protocoltypes.MessageHeaders hash cid.Cid } func (m *messageItem) Counter() uint64 { return m.headers.Counter } type simpleMessageQueue = queue.SimpleQueue[*messageItem] func newMessageQueue(name string, tracer queue.MetricsTracer[*messageItem]) *simpleMessageQueue { return queue.NewSimpleQueue[*messageItem](name, tracer) } type priorityMessageQueue = queue.PriorityQueue[*messageItem] func newPriorityMessageQueue(name string, tracer queue.MetricsTracer[*messageItem]) *priorityMessageQueue { return queue.NewPriorityQueue[*messageItem](name, tracer) } ================================================ FILE: store_message_test.go ================================================ package weshnet import ( "container/ring" "context" "fmt" "testing" "time" "github.com/libp2p/go-libp2p/p2p/host/eventbus" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ipfslog "berty.tech/go-ipfs-log" "berty.tech/weshnet/v2/pkg/protocoltypes" "berty.tech/weshnet/v2/pkg/testutil" ) func countEntries(out <-chan *protocoltypes.GroupMessageEvent) int { found := 0 for range out { found++ } return found } func Test_AddMessage_ListMessages_manually_supplying_secrets(t *testing.T) { testutil.FilterSpeed(t, testutil.Slow) ctx, cancel := context.WithCancel(context.Background()) defer cancel() memberCount := 2 deviceCount := 1 entriesCount := 25 testMsg1 := []byte("first message") peers, _, cleanup := CreatePeersWithGroupTest(ctx, t, "/tmp/message_test", memberCount, deviceCount) defer cleanup() dPK0 := peers[0].GC.DevicePubKey() ds0For1, err := peers[0].SecretStore.GetShareableChainKey(ctx, peers[0].GC.Group(), peers[1].GC.MemberPubKey()) require.NoError(t, err) require.NotNil(t, ds0For1) err = peers[1].SecretStore.RegisterChainKey(ctx, peers[0].GC.Group(), dPK0, ds0For1) require.NoError(t, err) _, err = peers[0].GC.MessageStore().AddMessage(ctx, testMsg1) require.NoError(t, err) <-time.After(time.Millisecond * 500) out, err := peers[0].GC.MessageStore().ListEvents(ctx, nil, nil, false) require.NoError(t, err) require.Equal(t, 1, countEntries(out)) watcherCtx, watcherCancel := context.WithTimeout(ctx, time.Second*5) chSub, err := peers[1].GC.MessageStore().EventBus().Subscribe(new(*protocoltypes.GroupMessageEvent)) require.NoError(t, err) defer chSub.Close() go func() { for { select { case <-chSub.Out(): case <-watcherCtx.Done(): return } c, err := peers[1].GC.MessageStore().ListEvents(watcherCtx, nil, nil, false) if !assert.NoError(t, err) { watcherCancel() break } if countEntries(c) == entriesCount+1 { watcherCancel() break } } }() for i := 0; i < entriesCount; i++ { payload := []byte(fmt.Sprintf("test message %d", i)) _, err = peers[0].GC.MessageStore().AddMessage(ctx, payload) require.NoError(t, err) } <-watcherCtx.Done() out, err = peers[1].GC.MessageStore().ListEvents(ctx, nil, nil, false) require.NoError(t, err) <-time.After(time.Second) require.Equal(t, 1+entriesCount, countEntries(out)) // TODO: check that ListEvents can be called multiple times with the same output // TODO: check that message are correctly ordered // TODO: check that message are correctly decrypted // TODO: check that message sender is correct // TODO: check that message parents IDs are valid // TODO: check that message IDs are valid } func bufferCount(buffer *ring.Ring) int { count := 0 buffer.Do(func(f any) { if _, ok := f.(ipfslog.Entry); ok { count++ } }) return count } func Test_Add_Messages_To_Cache(t *testing.T) { testutil.FilterSpeed(t, testutil.Fast) ctx, cancel := context.WithCancel(context.Background()) defer cancel() memberCount := 2 deviceCount := 1 entriesCount := 50 testMsg1 := []byte("last message") peers, _, cleanup := CreatePeersWithGroupTest(ctx, t, "/tmp/message_test", memberCount, deviceCount) defer cleanup() dPK0 := peers[0].GC.DevicePubKey() dPK0Raw, err := dPK0.Raw() require.NoError(t, err) ds0For1, err := peers[0].SecretStore.GetShareableChainKey(ctx, peers[0].GC.Group(), peers[1].GC.MemberPubKey()) require.NoError(t, err) require.NotNil(t, ds0For1) cevent, err := peers[0].GC.MessageStore().EventBus().Subscribe( new(*protocoltypes.GroupMessageEvent), eventbus.BufSize(entriesCount)) require.NoError(t, err) cadded, err := peers[1].GC.MessageStore().EventBus().Subscribe( new(messageItem), eventbus.BufSize(entriesCount)) require.NoError(t, err) for i := 0; i < entriesCount; i++ { payload := []byte(fmt.Sprintf("test message %d", i)) _, err = peers[0].GC.MessageStore().AddMessage(ctx, payload) require.NoError(t, err) } // check that all events has been received on peer 1 for i := 0; i < entriesCount; i++ { select { case <-cevent.Out(): case <-time.After(time.Second): require.FailNow(t, "timeout while waiting for group message event") return } } cevent.Close() clist, err := peers[0].GC.MessageStore().ListEvents(ctx, nil, nil, false) require.NoError(t, err) count := countEntries(clist) require.Equal(t, entriesCount, count) // check that messages has been replicated on peer 2 for i := 0; i < entriesCount; i++ { select { case <-cadded.Out(): case <-time.After(time.Second * 5): require.FailNow(t, "timeout while waiting for replicated event") return } } cadded.Close() // time.Sleep(time.Millisecond * 500) size, ok := peers[1].GC.MessageStore().CacheSizeForDevicePK(dPK0Raw) require.True(t, ok) require.Equal(t, entriesCount, size) err = peers[1].SecretStore.RegisterChainKey(ctx, peers[0].GC.Group(), dPK0, ds0For1) require.NoError(t, err) cevent, err = peers[1].GC.MessageStore().EventBus().Subscribe( new(*protocoltypes.GroupMessageEvent), eventbus.BufSize(entriesCount)) require.NoError(t, err) peers[1].GC.MessageStore().ProcessMessageQueueForDevicePK(ctx, dPK0Raw) // check that all events has been received on peer 2 for i := 0; i < entriesCount; i++ { select { case <-cevent.Out(): case <-time.After(time.Second): require.FailNow(t, "timeout while waiting for group message event") return } } cevent.Close() size, ok = peers[1].GC.MessageStore().CacheSizeForDevicePK(dPK0Raw) require.True(t, ok) require.Equal(t, 0, size) _, err = peers[0].GC.MessageStore().AddMessage(ctx, testMsg1) require.NoError(t, err) size, ok = peers[1].GC.MessageStore().CacheSizeForDevicePK(dPK0Raw) require.True(t, ok) require.Equal(t, 0, size) } ================================================ FILE: store_metadata.go ================================================ package weshnet import ( "context" crand "crypto/rand" "encoding/base64" "fmt" "io" "slices" "strings" coreiface "github.com/ipfs/kubo/core/coreiface" "github.com/libp2p/go-libp2p/core/crypto" "github.com/libp2p/go-libp2p/core/event" "github.com/libp2p/go-libp2p/p2p/host/eventbus" "go.uber.org/zap" "google.golang.org/protobuf/proto" ipfslog "berty.tech/go-ipfs-log" "berty.tech/go-ipfs-log/identityprovider" ipliface "berty.tech/go-ipfs-log/iface" "berty.tech/go-orbit-db/address" "berty.tech/go-orbit-db/iface" "berty.tech/go-orbit-db/stores" "berty.tech/go-orbit-db/stores/basestore" "berty.tech/go-orbit-db/stores/operation" "berty.tech/weshnet/v2/pkg/errcode" "berty.tech/weshnet/v2/pkg/protocoltypes" "berty.tech/weshnet/v2/pkg/secretstore" "berty.tech/weshnet/v2/pkg/tyber" ) type MetadataStore struct { basestore.BaseStore eventBus event.Bus emitters struct { groupMetadata event.Emitter metadataReceived event.Emitter } group *protocoltypes.Group memberDevice secretstore.OwnMemberDevice devicePublicKeyRaw []byte secretStore secretstore.SecretStore logger *zap.Logger ctx context.Context cancel context.CancelFunc } func isMultiMemberGroup(m *MetadataStore) bool { return m.group.GroupType == protocoltypes.GroupType_GroupTypeMultiMember } func isAccountGroup(m *MetadataStore) bool { return m.group.GroupType == protocoltypes.GroupType_GroupTypeAccount } func isContactGroup(m *MetadataStore) bool { return m.group.GroupType == protocoltypes.GroupType_GroupTypeContact } func (m *MetadataStore) typeChecker(types ...func(m *MetadataStore) bool) bool { for _, t := range types { if t(m) { return true } } return false } func (m *MetadataStore) setLogger(l *zap.Logger) { if l == nil { return } // m.logger = l.Named("store").With(logutil.PrivateString("group-id", fmt.Sprintf("%.6s", base64.StdEncoding.EncodeToString(m.group.PublicKey)))) m.logger = l.Named("metastore") if index, ok := m.Index().(loggable); ok { index.setLogger(m.logger) } } func openMetadataEntry(log ipfslog.Log, e ipfslog.Entry, g *protocoltypes.Group) (*protocoltypes.GroupMetadataEvent, proto.Message, error) { op, err := operation.ParseOperation(e) if err != nil { return nil, nil, err } meta, event, err := openGroupEnvelope(g, op.GetValue()) if err != nil { return nil, nil, err } metaEvent, err := newGroupMetadataEventFromEntry(log, e, meta, event, g) if err != nil { return nil, nil, err } return metaEvent, event, err } // not used // func (m *MetadataStore) openMetadataEntry(e ipfslog.Entry) (*protocoltypes.GroupMetadataEvent, proto.Message, error) { // return openMetadataEntry(m.OpLog(), e, m.group, m.devKS) // } // FIXME: use iterator instead to reduce resource usage (require go-ipfs-log improvements) func (m *MetadataStore) ListEvents(_ context.Context, since, until []byte, reverse bool) (<-chan *protocoltypes.GroupMetadataEvent, error) { entries, err := getEntriesInRange(m.OpLog().GetEntries().Reverse().Slice(), since, until) if err != nil { return nil, err } out := make(chan *protocoltypes.GroupMetadataEvent) go func() { iterateOverEntries( entries, reverse, func(entry ipliface.IPFSLogEntry) { event, _, err := openMetadataEntry(m.OpLog(), entry, m.group) if err != nil { m.logger.Error("unable to open metadata event", zap.Error(err)) } else { out <- event m.logger.Info("metadata store - sent 1 event from log history") } }, ) close(out) }() return out, nil } func (m *MetadataStore) AddDeviceToGroup(ctx context.Context) (operation.Operation, error) { md, err := m.secretStore.GetOwnMemberDeviceForGroup(m.group) if err != nil { return nil, errcode.ErrCode_ErrInternal.Wrap(err) } return MetadataStoreAddDeviceToGroup(ctx, m, m.group, md) } func MetadataStoreAddDeviceToGroup(ctx context.Context, m *MetadataStore, g *protocoltypes.Group, md secretstore.OwnMemberDevice) (operation.Operation, error) { device, err := md.Device().Raw() if err != nil { return nil, errcode.ErrCode_ErrSerialization.Wrap(err) } member, err := md.Member().Raw() if err != nil { return nil, errcode.ErrCode_ErrSerialization.Wrap(err) } k, err := m.GetMemberByDevice(md.Device()) if err == nil && k != nil { return nil, nil } memberSig, err := md.MemberSign(device) if err != nil { return nil, errcode.ErrCode_ErrCryptoSignature.Wrap(err) } event := &protocoltypes.GroupMemberDeviceAdded{ MemberPk: member, DevicePk: device, MemberSig: memberSig, } sig, err := signProtoWithDevice(event, md) if err != nil { return nil, errcode.ErrCode_ErrCryptoSignature.Wrap(err) } m.logger.Info("announcing device on store") return metadataStoreAddEvent(ctx, m, g, protocoltypes.EventType_EventTypeGroupMemberDeviceAdded, event, sig) } func (m *MetadataStore) SendSecret(ctx context.Context, memberPK crypto.PubKey) (operation.Operation, error) { ok, err := m.Index().(*metadataStoreIndex).areSecretsAlreadySent(memberPK) if err != nil { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(err) } if ok { return nil, errcode.ErrCode_ErrGroupSecretAlreadySentToMember } if devs, err := m.GetDevicesForMember(memberPK); len(devs) == 0 || err != nil { m.logger.Warn("sending secret to an unknown group member") } encryptedSecret, err := m.secretStore.GetShareableChainKey(ctx, m.group, memberPK) if err != nil { return nil, errcode.ErrCode_ErrCryptoEncrypt.Wrap(err) } return MetadataStoreSendSecret(ctx, m, m.group, m.memberDevice, memberPK, encryptedSecret) } func MetadataStoreSendSecret(ctx context.Context, m *MetadataStore, g *protocoltypes.Group, md secretstore.OwnMemberDevice, memberPK crypto.PubKey, encryptedSecret []byte) (operation.Operation, error) { devicePKRaw, err := md.Device().Raw() if err != nil { return nil, errcode.ErrCode_ErrSerialization.Wrap(err) } memberPKRaw, err := memberPK.Raw() if err != nil { return nil, errcode.ErrCode_ErrSerialization.Wrap(err) } event := &protocoltypes.GroupDeviceChainKeyAdded{ DevicePk: devicePKRaw, DestMemberPk: memberPKRaw, Payload: encryptedSecret, } sig, err := signProtoWithDevice(event, md) if err != nil { return nil, errcode.ErrCode_ErrCryptoSignature.Wrap(err) } return metadataStoreAddEvent(ctx, m, g, protocoltypes.EventType_EventTypeGroupDeviceChainKeyAdded, event, sig) } func (m *MetadataStore) ClaimGroupOwnership(ctx context.Context, groupSK crypto.PrivKey) (operation.Operation, error) { if !m.typeChecker(isMultiMemberGroup) { return nil, errcode.ErrCode_ErrGroupInvalidType } event := &protocoltypes.MultiMemberGroupInitialMemberAnnounced{ MemberPk: m.devicePublicKeyRaw, } sig, err := signProtoWithPrivateKey(event, groupSK) if err != nil { return nil, errcode.ErrCode_ErrCryptoSignature.Wrap(err) } return metadataStoreAddEvent(ctx, m, m.group, protocoltypes.EventType_EventTypeMultiMemberGroupInitialMemberAnnounced, event, sig) } func signProtoWithDevice(message proto.Message, memberDevice secretstore.OwnMemberDevice) ([]byte, error) { data, err := proto.Marshal(message) if err != nil { return nil, errcode.ErrCode_ErrSerialization.Wrap(err) } sig, err := memberDevice.DeviceSign(data) if err != nil { return nil, errcode.ErrCode_ErrCryptoSignature.Wrap(err) } return sig, nil } func signProtoWithPrivateKey(message proto.Message, sk crypto.PrivKey) ([]byte, error) { data, err := proto.Marshal(message) if err != nil { return nil, errcode.ErrCode_ErrSerialization.Wrap(err) } sig, err := sk.Sign(data) if err != nil { return nil, errcode.ErrCode_ErrCryptoSignature.Wrap(err) } return sig, nil } func metadataStoreAddEvent(ctx context.Context, m *MetadataStore, g *protocoltypes.Group, eventType protocoltypes.EventType, event proto.Message, sig []byte) (operation.Operation, error) { ctx, newTrace := tyber.ContextWithTraceID(ctx) tyberLogError := tyber.LogError if newTrace { m.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)...) tyberLogError = tyber.LogFatalError } env, err := sealGroupEnvelope(g, eventType, event, sig) if err != nil { return nil, tyberLogError(ctx, m.logger, "Failed to seal group envelope", errcode.ErrCode_ErrCryptoSignature.Wrap(err)) } m.logger.Debug(fmt.Sprintf("Sealed group envelope (%d bytes)", len(env)), tyber.FormatStepLogFields(ctx, []tyber.Detail{})...) op := operation.NewOperation(nil, "ADD", env) e, err := m.AddOperation(ctx, op, nil) if err != nil { return nil, tyberLogError(ctx, m.logger, "Failed to add operation on log", errcode.ErrCode_ErrOrbitDBAppend.Wrap(err)) } m.logger.Debug("Added operation on log", tyber.FormatStepLogFields(ctx, []tyber.Detail{ {Name: "CID", Description: e.GetHash().String()}, })...) op, err = operation.ParseOperation(e) if err != nil { return nil, tyberLogError(ctx, m.logger, "Failed to parse operation returned by log", errcode.ErrCode_ErrOrbitDBDeserialization.Wrap(err)) } if newTrace { m.logger.Debug("Added metadata on log successfully", tyber.FormatStepLogFields(ctx, []tyber.Detail{}, tyber.EndTrace)...) } return op, nil } func (m *MetadataStore) ListContacts() map[string]*AccountContact { return m.Index().(*metadataStoreIndex).listContacts() } func (m *MetadataStore) ListVerifiedCredentials() []*protocoltypes.AccountVerifiedCredentialRegistered { return m.Index().(*metadataStoreIndex).listVerifiedCredentials() } func (m *MetadataStore) GetMemberByDevice(pk crypto.PubKey) (crypto.PubKey, error) { return m.Index().(*metadataStoreIndex).getMemberByDevice(pk) } func (m *MetadataStore) GetDevicesForMember(pk crypto.PubKey) ([]crypto.PubKey, error) { return m.Index().(*metadataStoreIndex).getDevicesForMember(pk) } func (m *MetadataStore) ListAdmins() []crypto.PubKey { if m.typeChecker(isContactGroup, isAccountGroup) { return m.ListMembers() } return m.Index().(*metadataStoreIndex).listAdmins() } func (m *MetadataStore) GetIncomingContactRequestsStatus() (bool, *protocoltypes.ShareableContact) { if !m.typeChecker(isAccountGroup) { return false, nil } enabled := m.Index().(*metadataStoreIndex).contactRequestsEnabled() seed := m.Index().(*metadataStoreIndex).contactRequestsSeed() rawMemberDevice, err := m.memberDevice.Member().Raw() if err != nil { m.logger.Error("unable to serialize member public key", zap.Error(err)) return enabled, nil } contactRef := &protocoltypes.ShareableContact{ Pk: rawMemberDevice, PublicRendezvousSeed: seed, } return enabled, contactRef } func (m *MetadataStore) ListMembers() []crypto.PubKey { if m.typeChecker(isAccountGroup, isContactGroup, isMultiMemberGroup) { return m.Index().(*metadataStoreIndex).listMembers() } return nil } func (m *MetadataStore) ListDevices() []crypto.PubKey { return m.Index().(*metadataStoreIndex).listDevices() } func (m *MetadataStore) ListMultiMemberGroups() []*protocoltypes.Group { if !m.typeChecker(isAccountGroup) { return nil } idx, ok := m.Index().(*metadataStoreIndex) if !ok { return nil } idx.lock.Lock() defer idx.lock.Unlock() groups := []*protocoltypes.Group(nil) for _, g := range idx.groups { if g.state != accountGroupJoinedStateJoined { continue } groups = append(groups, g.group) } return groups } func (m *MetadataStore) ListOtherMembersDevices() []crypto.PubKey { return m.Index().(*metadataStoreIndex).listOtherMembersDevices() } func (m *MetadataStore) GetRequestOwnMetadataForContact(pk []byte) ([]byte, error) { idx, ok := m.Index().(*metadataStoreIndex) if !ok { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("invalid index type")) } idx.lock.Lock() defer idx.lock.Unlock() meta, ok := idx.contactRequestMetadata[string(pk)] if !ok { return nil, errcode.ErrCode_ErrMissingMapKey.Wrap(fmt.Errorf("no metadata found for specified contact")) } return meta, nil } func (m *MetadataStore) ListContactsByStatus(states ...protocoltypes.ContactState) []*protocoltypes.ShareableContact { if !m.typeChecker(isAccountGroup) { return nil } idx, ok := m.Index().(*metadataStoreIndex) if !ok { return nil } idx.lock.Lock() defer idx.lock.Unlock() contacts := []*protocoltypes.ShareableContact(nil) for _, c := range idx.contacts { hasState := slices.Contains(states, c.state) if hasState { contacts = append(contacts, c.contact) } } return contacts } func (m *MetadataStore) GetContactFromGroupPK(groupPK []byte) *protocoltypes.ShareableContact { if !m.typeChecker(isAccountGroup) { return nil } idx, ok := m.Index().(*metadataStoreIndex) if !ok { return nil } idx.lock.Lock() defer idx.lock.Unlock() contact, ok := idx.contactsFromGroupPK[string(groupPK)] if !ok || contact == nil { return nil } return contact.contact } func (m *MetadataStore) checkIfInGroup(pk []byte) bool { idx, ok := m.Index().(*metadataStoreIndex) if !ok { return false } idx.lock.Lock() defer idx.lock.Unlock() if existingGroup, ok := idx.groups[string(pk)]; ok && existingGroup.state == accountGroupJoinedStateJoined { return true } return false } // GroupJoin indicates the payload includes that the deviceKeystore has joined a group func (m *MetadataStore) GroupJoin(ctx context.Context, g *protocoltypes.Group) (operation.Operation, error) { if !m.typeChecker(isAccountGroup) { return nil, errcode.ErrCode_ErrGroupInvalidType } if err := g.IsValid(); err != nil { return nil, errcode.ErrCode_ErrDeserialization.Wrap(err) } if m.checkIfInGroup(g.PublicKey) { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("already present in group")) } return m.attributeSignAndAddEvent(ctx, &protocoltypes.AccountGroupJoined{ Group: g, }, protocoltypes.EventType_EventTypeAccountGroupJoined) } // GroupLeave indicates the payload includes that the deviceKeystore has left a group func (m *MetadataStore) GroupLeave(ctx context.Context, pk crypto.PubKey) (operation.Operation, error) { if !m.typeChecker(isAccountGroup) { return nil, errcode.ErrCode_ErrGroupInvalidType } if pk == nil { return nil, errcode.ErrCode_ErrInvalidInput } bytes, err := pk.Raw() if err != nil { return nil, errcode.ErrCode_ErrSerialization.Wrap(err) } if !m.checkIfInGroup(bytes) { return nil, errcode.ErrCode_ErrInvalidInput } return m.groupAction(ctx, pk, &protocoltypes.AccountGroupLeft{}, protocoltypes.EventType_EventTypeAccountGroupLeft) } // ContactRequestDisable indicates the payload includes that the deviceKeystore has disabled incoming contact requests func (m *MetadataStore) ContactRequestDisable(ctx context.Context) (operation.Operation, error) { if !m.typeChecker(isAccountGroup) { return nil, errcode.ErrCode_ErrGroupInvalidType } return m.attributeSignAndAddEvent(ctx, &protocoltypes.AccountContactRequestDisabled{}, protocoltypes.EventType_EventTypeAccountContactRequestDisabled) } // ContactRequestEnable indicates the payload includes that the deviceKeystore has enabled incoming contact requests func (m *MetadataStore) ContactRequestEnable(ctx context.Context) (operation.Operation, error) { if !m.typeChecker(isAccountGroup) { return nil, errcode.ErrCode_ErrGroupInvalidType } return m.attributeSignAndAddEvent(ctx, &protocoltypes.AccountContactRequestEnabled{}, protocoltypes.EventType_EventTypeAccountContactRequestEnabled) } // ContactRequestReferenceReset indicates the payload includes that the deviceKeystore has a new contact request reference func (m *MetadataStore) ContactRequestReferenceReset(ctx context.Context) (operation.Operation, error) { if !m.typeChecker(isAccountGroup) { return nil, errcode.ErrCode_ErrGroupInvalidType } seed, err := genNewSeed() if err != nil { return nil, errcode.ErrCode_ErrCryptoKeyGeneration.Wrap(err) } return m.attributeSignAndAddEvent(ctx, &protocoltypes.AccountContactRequestReferenceReset{ PublicRendezvousSeed: seed, }, protocoltypes.EventType_EventTypeAccountContactRequestReferenceReset) } // ContactRequestOutgoingEnqueue indicates the payload includes that the deviceKeystore will attempt to send a new contact request func (m *MetadataStore) ContactRequestOutgoingEnqueue(ctx context.Context, contact *protocoltypes.ShareableContact, ownMetadata []byte) (operation.Operation, error) { ctx, _ = tyber.ContextWithTraceID(ctx) b64GroupPK := base64.RawURLEncoding.EncodeToString(m.group.PublicKey) m.logger.Debug("Enqueuing contact request", tyber.FormatStepLogFields(ctx, []tyber.Detail{{Name: "GroupPK", Description: fmt.Sprint(b64GroupPK)}})...) if !m.typeChecker(isAccountGroup) { return nil, errcode.ErrCode_ErrGroupInvalidType } if err := contact.CheckFormat(); err != nil { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(err) } accountPublicKey := m.memberDevice.Member() if contact.IsSamePK(accountPublicKey) { return nil, errcode.ErrCode_ErrContactRequestSameAccount } pk, err := contact.GetPubKey() if err != nil { return nil, errcode.ErrCode_ErrDeserialization.Wrap(err) } if m.checkContactStatus(pk, protocoltypes.ContactState_ContactStateAdded) { return nil, errcode.ErrCode_ErrContactRequestContactAlreadyAdded } if m.checkContactStatus(pk, protocoltypes.ContactState_ContactStateRemoved, protocoltypes.ContactState_ContactStateDiscarded, protocoltypes.ContactState_ContactStateReceived) { return m.ContactRequestOutgoingSent(ctx, pk) } op, err := m.attributeSignAndAddEvent(ctx, &protocoltypes.AccountContactRequestOutgoingEnqueued{ Contact: &protocoltypes.ShareableContact{ Pk: contact.Pk, PublicRendezvousSeed: contact.PublicRendezvousSeed, Metadata: contact.Metadata, }, OwnMetadata: ownMetadata, }, protocoltypes.EventType_EventTypeAccountContactRequestOutgoingEnqueued) m.logger.Debug("Enqueued contact request", tyber.FormatStepLogFields(ctx, []tyber.Detail{})...) return op, err } // ContactRequestOutgoingSent indicates the payload includes that the deviceKeystore has sent a contact request func (m *MetadataStore) ContactRequestOutgoingSent(ctx context.Context, pk crypto.PubKey) (operation.Operation, error) { if !m.typeChecker(isAccountGroup) { return nil, errcode.ErrCode_ErrGroupInvalidType } switch m.getContactStatus(pk) { case protocoltypes.ContactState_ContactStateToRequest: case protocoltypes.ContactState_ContactStateReceived: case protocoltypes.ContactState_ContactStateRemoved: case protocoltypes.ContactState_ContactStateDiscarded: case protocoltypes.ContactState_ContactStateUndefined: return nil, errcode.ErrCode_ErrContactRequestContactUndefined case protocoltypes.ContactState_ContactStateAdded: return nil, errcode.ErrCode_ErrContactRequestContactAlreadyAdded case protocoltypes.ContactState_ContactStateBlocked: return nil, errcode.ErrCode_ErrContactRequestContactBlocked default: return nil, errcode.ErrCode_ErrInvalidInput } return m.contactAction(ctx, pk, &protocoltypes.AccountContactRequestOutgoingSent{}, protocoltypes.EventType_EventTypeAccountContactRequestOutgoingSent) } // ContactRequestIncomingReceived indicates the payload includes that the deviceKeystore has received a contact request func (m *MetadataStore) ContactRequestIncomingReceived(ctx context.Context, contact *protocoltypes.ShareableContact) (operation.Operation, error) { m.logger.Debug("Sending ContactRequestIncomingReceived on Account group", tyber.FormatStepLogFields(ctx, []tyber.Detail{})...) if !m.typeChecker(isAccountGroup) { return nil, errcode.ErrCode_ErrGroupInvalidType } if err := contact.CheckFormat(protocoltypes.ShareableContactOptionsAllowMissingRDVSeed); err != nil { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(err) } accountPublicKey := m.memberDevice.Member() if contact.IsSamePK(accountPublicKey) { return nil, errcode.ErrCode_ErrContactRequestSameAccount } pk, err := contact.GetPubKey() if err != nil { return nil, errcode.ErrCode_ErrDeserialization.Wrap(err) } switch m.getContactStatus(pk) { case protocoltypes.ContactState_ContactStateUndefined: case protocoltypes.ContactState_ContactStateRemoved: case protocoltypes.ContactState_ContactStateDiscarded: // If incoming request comes from an account for which an outgoing request // is in "sending" state, mark the outgoing request as "sent" case protocoltypes.ContactState_ContactStateToRequest: return m.ContactRequestOutgoingSent(ctx, pk) // Errors case protocoltypes.ContactState_ContactStateReceived: return nil, errcode.ErrCode_ErrContactRequestIncomingAlreadyReceived case protocoltypes.ContactState_ContactStateAdded: return nil, errcode.ErrCode_ErrContactRequestContactAlreadyAdded case protocoltypes.ContactState_ContactStateBlocked: return nil, errcode.ErrCode_ErrContactRequestContactBlocked default: return nil, errcode.ErrCode_ErrInvalidInput } return m.attributeSignAndAddEvent(ctx, &protocoltypes.AccountContactRequestIncomingReceived{ ContactPk: contact.Pk, ContactRendezvousSeed: contact.PublicRendezvousSeed, ContactMetadata: contact.Metadata, }, protocoltypes.EventType_EventTypeAccountContactRequestIncomingReceived) } // ContactRequestIncomingDiscard indicates the payload includes that the deviceKeystore has ignored a contact request func (m *MetadataStore) ContactRequestIncomingDiscard(ctx context.Context, pk crypto.PubKey) (operation.Operation, error) { if !m.typeChecker(isAccountGroup) { return nil, errcode.ErrCode_ErrGroupInvalidType } if !m.checkContactStatus(pk, protocoltypes.ContactState_ContactStateReceived) { return nil, errcode.ErrCode_ErrInvalidInput } return m.contactAction(ctx, pk, &protocoltypes.AccountContactRequestIncomingDiscarded{}, protocoltypes.EventType_EventTypeAccountContactRequestIncomingDiscarded) } // ContactRequestIncomingAccept indicates the payload includes that the deviceKeystore has accepted a contact request func (m *MetadataStore) ContactRequestIncomingAccept(ctx context.Context, pk crypto.PubKey) (operation.Operation, error) { if !m.typeChecker(isAccountGroup) { return nil, errcode.ErrCode_ErrGroupInvalidType } if !m.checkContactStatus(pk, protocoltypes.ContactState_ContactStateReceived) { return nil, errcode.ErrCode_ErrInvalidInput } return m.contactAction(ctx, pk, &protocoltypes.AccountContactRequestIncomingAccepted{}, protocoltypes.EventType_EventTypeAccountContactRequestIncomingAccepted) } // ContactBlock indicates the payload includes that the deviceKeystore has blocked a contact func (m *MetadataStore) ContactBlock(ctx context.Context, pk crypto.PubKey) (operation.Operation, error) { if !m.typeChecker(isAccountGroup) { return nil, errcode.ErrCode_ErrGroupInvalidType } accountPublicKey := m.memberDevice.Member() if accountPublicKey.Equals(pk) { return nil, errcode.ErrCode_ErrInvalidInput } if m.checkContactStatus(pk, protocoltypes.ContactState_ContactStateBlocked) { return nil, errcode.ErrCode_ErrInvalidInput } return m.contactAction(ctx, pk, &protocoltypes.AccountContactBlocked{}, protocoltypes.EventType_EventTypeAccountContactBlocked) } // ContactUnblock indicates the payload includes that the deviceKeystore has unblocked a contact func (m *MetadataStore) ContactUnblock(ctx context.Context, pk crypto.PubKey) (operation.Operation, error) { if !m.typeChecker(isAccountGroup) { return nil, errcode.ErrCode_ErrGroupInvalidType } if !m.checkContactStatus(pk, protocoltypes.ContactState_ContactStateBlocked) { return nil, errcode.ErrCode_ErrInvalidInput } return m.contactAction(ctx, pk, &protocoltypes.AccountContactUnblocked{}, protocoltypes.EventType_EventTypeAccountContactUnblocked) } func (m *MetadataStore) ContactSendAliasKey(ctx context.Context) (operation.Operation, error) { if !m.typeChecker(isContactGroup) { return nil, errcode.ErrCode_ErrGroupInvalidType } accountProofPublicKey, err := m.secretStore.GetAccountProofPublicKey() if err != nil { return nil, errcode.ErrCode_ErrInternal.Wrap(err) } alias, err := accountProofPublicKey.Raw() if err != nil { return nil, errcode.ErrCode_ErrInternal.Wrap(err) } return m.attributeSignAndAddEvent(ctx, &protocoltypes.ContactAliasKeyAdded{ AliasPk: alias, }, protocoltypes.EventType_EventTypeContactAliasKeyAdded) } func (m *MetadataStore) SendAliasProof(ctx context.Context) (operation.Operation, error) { if !m.typeChecker(isMultiMemberGroup) { return nil, errcode.ErrCode_ErrGroupInvalidType } resolver := []byte(nil) // TODO: should be a hmac value of something for quicker searches proof := []byte(nil) // TODO: should be a signed value of something return m.attributeSignAndAddEvent(ctx, &protocoltypes.MultiMemberGroupAliasResolverAdded{ AliasResolver: resolver, AliasProof: proof, }, protocoltypes.EventType_EventTypeMultiMemberGroupAliasResolverAdded) } func (m *MetadataStore) SendAppMetadata(ctx context.Context, message []byte) (operation.Operation, error) { return m.attributeSignAndAddEvent(ctx, &protocoltypes.GroupMetadataPayloadSent{ Message: message, }, protocoltypes.EventType_EventTypeGroupMetadataPayloadSent) } func (m *MetadataStore) SendAccountVerifiedCredentialAdded(ctx context.Context, token *protocoltypes.AccountVerifiedCredentialRegistered) (operation.Operation, error) { if !m.typeChecker(isAccountGroup) { return nil, errcode.ErrCode_ErrGroupInvalidType } return m.attributeSignAndAddEvent(ctx, token, protocoltypes.EventType_EventTypeAccountVerifiedCredentialRegistered) } func (m *MetadataStore) SendGroupReplicating(ctx context.Context, authenticationURL, replicationServer string) (operation.Operation, error) { return m.attributeSignAndAddEvent(ctx, &protocoltypes.GroupReplicating{ AuthenticationUrl: authenticationURL, ReplicationServer: replicationServer, }, protocoltypes.EventType_EventTypeGroupReplicating) } type accountSignableEvent interface { proto.Message SetDevicePK([]byte) } type accountContactEvent interface { accountSignableEvent SetContactPK([]byte) } type accountGroupEvent interface { accountSignableEvent SetGroupPK([]byte) } func (m *MetadataStore) attributeSignAndAddEvent(ctx context.Context, evt accountSignableEvent, eventType protocoltypes.EventType) (operation.Operation, error) { evt.SetDevicePK(m.devicePublicKeyRaw) sig, err := signProtoWithDevice(evt, m.memberDevice) if err != nil { return nil, errcode.ErrCode_ErrCryptoSignature.Wrap(err) } m.logger.Debug("Signed event", tyber.FormatStepLogFields(ctx, []tyber.Detail{{Name: "Signature", Description: base64.RawURLEncoding.EncodeToString(sig)}})...) return metadataStoreAddEvent(ctx, m, m.group, eventType, evt, sig) } func (m *MetadataStore) contactAction(ctx context.Context, pk crypto.PubKey, event accountContactEvent, evtType protocoltypes.EventType) (operation.Operation, error) { ctx, newTrace := tyber.ContextWithTraceID(ctx) var tyberFields []zap.Field if newTrace { tyberFields = tyber.FormatTraceLogFields(ctx) } else { tyberFields = tyber.FormatStepLogFields(ctx, []tyber.Detail{}) } m.logger.Debug("Sending "+strings.TrimPrefix(evtType.String(), "EventType")+" on Account group", tyberFields...) if pk == nil || event == nil { return nil, errcode.ErrCode_ErrInvalidInput } pkBytes, err := pk.Raw() if err != nil { return nil, errcode.ErrCode_ErrSerialization.Wrap(err) } event.SetContactPK(pkBytes) op, err := m.attributeSignAndAddEvent(ctx, event, evtType) if err != nil { return nil, err } if newTrace { m.logger.Debug("Event added successfully", tyber.FormatStepLogFields(ctx, []tyber.Detail{}, tyber.EndTrace)...) } return op, nil } func (m *MetadataStore) groupAction(ctx context.Context, pk crypto.PubKey, event accountGroupEvent, evtType protocoltypes.EventType) (operation.Operation, error) { pkBytes, err := pk.Raw() if err != nil { return nil, errcode.ErrCode_ErrSerialization.Wrap(err) } event.SetGroupPK(pkBytes) return m.attributeSignAndAddEvent(ctx, event, evtType) } func (m *MetadataStore) getContactStatus(pk crypto.PubKey) protocoltypes.ContactState { if pk == nil { return protocoltypes.ContactState_ContactStateUndefined } contact, err := m.Index().(*metadataStoreIndex).getContact(pk) if err != nil { m.logger.Warn("unable to get contact for public key", zap.Error(err)) return protocoltypes.ContactState_ContactStateUndefined } return contact.state } func (m *MetadataStore) checkContactStatus(pk crypto.PubKey, states ...protocoltypes.ContactState) bool { contactStatus := m.getContactStatus(pk) return slices.Contains(states, contactStatus) } type EventMetadataReceived struct { MetaEvent *protocoltypes.GroupMetadataEvent Event proto.Message } func constructorFactoryGroupMetadata(s *WeshOrbitDB, logger *zap.Logger) iface.StoreConstructor { return func(ipfs coreiface.CoreAPI, identity *identityprovider.Identity, addr address.Address, options *iface.NewStoreOptions) (iface.Store, error) { g, err := s.getGroupFromOptions(options) if err != nil { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(err) } shortGroupType := strings.TrimPrefix(g.GetGroupType().String(), "GroupType") b64GroupPK := base64.RawURLEncoding.EncodeToString(g.PublicKey) replication := false if options.EventBus == nil { options.EventBus = eventbus.NewBus() } store := &MetadataStore{ eventBus: options.EventBus, group: g, logger: logger, secretStore: s.secretStore, } if s.replicationMode { replication = true } else { var err error store.memberDevice, err = s.secretStore.GetOwnMemberDeviceForGroup(g) if err != nil { if errcode.Is(err, errcode.ErrCode_ErrInvalidInput) { replication = true } else { return nil, errcode.ErrCode_ErrOrbitDBInit.Wrap(err) } } else { store.devicePublicKeyRaw, err = store.memberDevice.Device().Raw() if err != nil { return nil, errcode.ErrCode_ErrOrbitDBInit.Wrap(err) } } } store.ctx, store.cancel = context.WithCancel(context.Background()) if err := store.initEmitter(); err != nil { return nil, fmt.Errorf("unable to init emitters: %w", err) } if replication { options.Index = basestore.NewNoopIndex if err := store.InitBaseStore(ipfs, identity, addr, options); err != nil { store.cancel() return nil, errcode.ErrCode_ErrOrbitDBInit.Wrap(err) } return store, nil } chSub, err := store.eventBus.Subscribe([]any{ new(stores.EventWrite), new(stores.EventReplicated), }, eventbus.BufSize(128)) if err != nil { store.cancel() return nil, fmt.Errorf("unable to subscribe to store events") } // Enable logs in the metadata index store.setLogger(logger) go func(ctx context.Context) { defer chSub.Close() for { var e any select { case e = <-chSub.Out(): case <-ctx.Done(): return } var entries []ipfslog.Entry switch evt := e.(type) { case stores.EventWrite: entries = []ipfslog.Entry{evt.Entry} case stores.EventReplicated: entries = evt.Entries } for _, entry := range entries { ctx = tyber.ContextWithConstantTraceID(ctx, "msgrcvd-"+entry.GetHash().String()) tyber.LogTraceStart(ctx, store.logger, fmt.Sprintf("Received metadata from %s group %s", shortGroupType, b64GroupPK)) metaEvent, event, err := openMetadataEntry(store.OpLog(), entry, g) if err != nil { _ = tyber.LogFatalError(ctx, store.logger, "Unable to open metadata event", err, tyber.WithDetail("RawEvent", fmt.Sprint(e)), tyber.ForceReopen) continue } tyber.LogStep(ctx, store.logger, "Opened metadata store event", tyber.ForceReopen, tyber.EndTrace, tyber.WithJSONDetail("MetaEvent", metaEvent), tyber.WithJSONDetail("Event", event), tyber.UpdateTraceName(fmt.Sprintf("Received %s from %s group %s", strings.TrimPrefix(metaEvent.GetMetadata().GetEventType().String(), "EventType"), shortGroupType, b64GroupPK)), ) recvEvent := EventMetadataReceived{ MetaEvent: metaEvent, Event: event, } if err := store.emitters.metadataReceived.Emit(recvEvent); err != nil { store.logger.Warn("unable to emit recv event", zap.Error(err)) } if err := store.emitters.groupMetadata.Emit(metaEvent); err != nil { store.logger.Warn("unable to emit group metadata event", zap.Error(err)) } } } }(store.ctx) options.Index = newMetadataIndex(store.ctx, g, store.memberDevice, s.secretStore) if err := store.InitBaseStore(ipfs, identity, addr, options); err != nil { store.cancel() return nil, errcode.ErrCode_ErrOrbitDBInit.Wrap(err) } return store, nil } } func (m *MetadataStore) initEmitter() (err error) { if m.emitters.metadataReceived, err = m.eventBus.Emitter(new(EventMetadataReceived)); err != nil { return err } if m.emitters.groupMetadata, err = m.eventBus.Emitter(new(*protocoltypes.GroupMetadataEvent)); err != nil { return err } return err } func genNewSeed() (seed []byte, err error) { seed, err = io.ReadAll(io.LimitReader(crand.Reader, protocoltypes.RendezvousSeedLength)) return seed, err } func (m *MetadataStore) Close() error { m.cancel() return m.BaseStore.Close() } ================================================ FILE: store_metadata_index.go ================================================ package weshnet import ( "context" "fmt" "sync" "github.com/libp2p/go-libp2p/core/crypto" "go.uber.org/zap" "google.golang.org/protobuf/proto" ipfslog "berty.tech/go-ipfs-log" "berty.tech/go-orbit-db/iface" "berty.tech/weshnet/v2/pkg/cryptoutil" "berty.tech/weshnet/v2/pkg/errcode" "berty.tech/weshnet/v2/pkg/protocoltypes" "berty.tech/weshnet/v2/pkg/secretstore" ) // FIXME: replace members, devices, sentSecrets, contacts and groups by a circular buffer to avoid an attack by RAM saturation type metadataStoreIndex struct { members map[string][]secretstore.MemberDevice devices map[string]secretstore.MemberDevice handledEvents map[string]struct{} sentSecrets map[string]struct{} admins map[crypto.PubKey]struct{} contacts map[string]*AccountContact contactsFromGroupPK map[string]*AccountContact groups map[string]*accountGroup contactRequestMetadata map[string][]byte verifiedCredentials []*protocoltypes.AccountVerifiedCredentialRegistered contactRequestSeed []byte contactRequestEnabled *bool eventHandlers map[protocoltypes.EventType][]func(event proto.Message) error postIndexActions []func() error eventsContactAddAliasKey []*protocoltypes.ContactAliasKeyAdded ownAliasKeySent bool otherAliasKey []byte group *protocoltypes.Group ownMemberDevice secretstore.MemberDevice secretStore secretstore.SecretStore ctx context.Context lock sync.RWMutex logger *zap.Logger } //nolint:revive func (m *metadataStoreIndex) Get(key string) any { return nil } func (m *metadataStoreIndex) setLogger(logger *zap.Logger) { if logger == nil { return } m.logger = logger } func (m *metadataStoreIndex) UpdateIndex(log ipfslog.Log, _ []ipfslog.Entry) error { m.lock.Lock() defer m.lock.Unlock() entries := log.GetEntries().Slice() // Resetting state m.contacts = map[string]*AccountContact{} m.contactsFromGroupPK = map[string]*AccountContact{} m.groups = map[string]*accountGroup{} m.contactRequestMetadata = map[string][]byte{} m.contactRequestEnabled = nil m.contactRequestSeed = []byte(nil) m.verifiedCredentials = nil m.handledEvents = map[string]struct{}{} for i := len(entries) - 1; i >= 0; i-- { e := entries[i] _, alreadyHandledEvent := m.handledEvents[e.GetHash().String()] // TODO: improve account events handling if m.group.GroupType != protocoltypes.GroupType_GroupTypeAccount && alreadyHandledEvent { continue } metaEvent, event, err := openMetadataEntry(log, e, m.group) if err != nil { m.logger.Error("unable to open metadata entry", zap.Error(err)) continue } handlers, ok := m.eventHandlers[metaEvent.Metadata.EventType] if !ok { m.handledEvents[e.GetHash().String()] = struct{}{} m.logger.Error("handler for event type not found", zap.String("event-type", metaEvent.Metadata.EventType.String())) continue } var lastErr error for _, h := range handlers { err = h(event) if err != nil { m.logger.Error("unable to handle event", zap.Error(err)) lastErr = err } } if lastErr != nil { m.handledEvents[e.GetHash().String()] = struct{}{} continue } m.handledEvents[e.GetHash().String()] = struct{}{} } for _, h := range m.postIndexActions { if err := h(); err != nil { return errcode.ErrCode_ErrInternal.Wrap(err) } } return nil } func (m *metadataStoreIndex) handleGroupMemberDeviceAdded(event proto.Message) error { e, ok := event.(*protocoltypes.GroupMemberDeviceAdded) if !ok { return errcode.ErrCode_ErrInvalidInput } member, err := crypto.UnmarshalEd25519PublicKey(e.MemberPk) if err != nil { return errcode.ErrCode_ErrDeserialization.Wrap(err) } device, err := crypto.UnmarshalEd25519PublicKey(e.DevicePk) if err != nil { return errcode.ErrCode_ErrDeserialization.Wrap(err) } if _, ok := m.devices[string(e.DevicePk)]; ok { return nil } memberDevice := secretstore.NewMemberDevice(member, device) m.devices[string(e.DevicePk)] = memberDevice m.members[string(e.MemberPk)] = append(m.members[string(e.MemberPk)], memberDevice) return nil } func (m *metadataStoreIndex) handleGroupDeviceChainKeyAdded(event proto.Message) error { e, ok := event.(*protocoltypes.GroupDeviceChainKeyAdded) if !ok { return errcode.ErrCode_ErrInvalidInput } _, err := crypto.UnmarshalEd25519PublicKey(e.DestMemberPk) if err != nil { return errcode.ErrCode_ErrDeserialization.Wrap(err) } senderPK, err := crypto.UnmarshalEd25519PublicKey(e.DevicePk) if err != nil { return errcode.ErrCode_ErrDeserialization.Wrap(err) } if m.ownMemberDevice.Device().Equals(senderPK) { m.sentSecrets[string(e.DestMemberPk)] = struct{}{} } return nil } func (m *metadataStoreIndex) getMemberByDevice(devicePublicKey crypto.PubKey) (crypto.PubKey, error) { m.lock.RLock() defer m.lock.RUnlock() publicKeyBytes, err := devicePublicKey.Raw() if err != nil { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(err) } return m.unsafeGetMemberByDevice(publicKeyBytes) } func (m *metadataStoreIndex) unsafeGetMemberByDevice(publicKeyBytes []byte) (crypto.PubKey, error) { if l := len(publicKeyBytes); l != cryptoutil.KeySize { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("invalid private key size, expected %d got %d", cryptoutil.KeySize, l)) } device, ok := m.devices[string(publicKeyBytes)] if !ok { return nil, errcode.ErrCode_ErrMissingInput } return device.Member(), nil } func (m *metadataStoreIndex) getDevicesForMember(pk crypto.PubKey) ([]crypto.PubKey, error) { m.lock.RLock() defer m.lock.RUnlock() id, err := pk.Raw() if err != nil { return nil, errcode.ErrCode_ErrInvalidInput.Wrap(err) } mds, ok := m.members[string(id)] if !ok { return nil, errcode.ErrCode_ErrInvalidInput } ret := make([]crypto.PubKey, len(mds)) for i, md := range mds { ret[i] = md.Device() } return ret, nil } func (m *metadataStoreIndex) MemberCount() int { m.lock.RLock() defer m.lock.RUnlock() return len(m.members) } func (m *metadataStoreIndex) DeviceCount() int { m.lock.RLock() defer m.lock.RUnlock() return len(m.devices) } func (m *metadataStoreIndex) listContacts() map[string]*AccountContact { m.lock.RLock() defer m.lock.RUnlock() contacts := make(map[string]*AccountContact) for k, contact := range m.contacts { contacts[k] = &AccountContact{ state: contact.state, contact: &protocoltypes.ShareableContact{ Pk: contact.contact.Pk, PublicRendezvousSeed: contact.contact.PublicRendezvousSeed, Metadata: contact.contact.Metadata, }, } } return contacts } func (m *metadataStoreIndex) listVerifiedCredentials() []*protocoltypes.AccountVerifiedCredentialRegistered { m.lock.RLock() defer m.lock.RUnlock() return m.verifiedCredentials } func (m *metadataStoreIndex) listMembers() []crypto.PubKey { m.lock.RLock() defer m.lock.RUnlock() members := make([]crypto.PubKey, len(m.members)) i := 0 for _, md := range m.members { members[i] = md[0].Member() i++ } return members } func (m *metadataStoreIndex) listDevices() []crypto.PubKey { m.lock.RLock() defer m.lock.RUnlock() devices := make([]crypto.PubKey, len(m.devices)) i := 0 for _, md := range m.devices { devices[i] = md.Device() i++ } return devices } func (m *metadataStoreIndex) areSecretsAlreadySent(pk crypto.PubKey) (bool, error) { m.lock.RLock() defer m.lock.RUnlock() key, err := pk.Raw() if err != nil { return false, errcode.ErrCode_ErrInvalidInput.Wrap(err) } _, ok := m.sentSecrets[string(key)] return ok, nil } type accountGroupJoinedState uint32 const ( accountGroupJoinedStateJoined accountGroupJoinedState = iota + 1 accountGroupJoinedStateLeft ) type accountGroup struct { state accountGroupJoinedState group *protocoltypes.Group } type AccountContact struct { state protocoltypes.ContactState contact *protocoltypes.ShareableContact } func (m *metadataStoreIndex) handleGroupJoined(event proto.Message) error { evt, ok := event.(*protocoltypes.AccountGroupJoined) if !ok { return errcode.ErrCode_ErrInvalidInput } _, ok = m.groups[string(evt.Group.PublicKey)] if ok { return nil } m.groups[string(evt.Group.PublicKey)] = &accountGroup{ group: evt.Group, state: accountGroupJoinedStateJoined, } return nil } func (m *metadataStoreIndex) handleGroupLeft(event proto.Message) error { evt, ok := event.(*protocoltypes.AccountGroupLeft) if !ok { return errcode.ErrCode_ErrInvalidInput } _, ok = m.groups[string(evt.GroupPk)] if ok { return nil } m.groups[string(evt.GroupPk)] = &accountGroup{ state: accountGroupJoinedStateLeft, } return nil } func (m *metadataStoreIndex) handleContactRequestDisabled(event proto.Message) error { if m.contactRequestEnabled != nil { return nil } _, ok := event.(*protocoltypes.AccountContactRequestDisabled) if !ok { return errcode.ErrCode_ErrInvalidInput } f := false m.contactRequestEnabled = &f return nil } func (m *metadataStoreIndex) handleContactRequestEnabled(event proto.Message) error { if m.contactRequestEnabled != nil { return nil } _, ok := event.(*protocoltypes.AccountContactRequestEnabled) if !ok { return errcode.ErrCode_ErrInvalidInput } t := true m.contactRequestEnabled = &t return nil } func (m *metadataStoreIndex) handleContactRequestReferenceReset(event proto.Message) error { evt, ok := event.(*protocoltypes.AccountContactRequestReferenceReset) if !ok { return errcode.ErrCode_ErrInvalidInput } if m.contactRequestSeed != nil { return nil } m.contactRequestSeed = evt.PublicRendezvousSeed return nil } func (m *metadataStoreIndex) registerContactFromGroupPK(ac *AccountContact) error { if m.group.GroupType != protocoltypes.GroupType_GroupTypeAccount { return errcode.ErrCode_ErrGroupInvalidType } contactPK, err := crypto.UnmarshalEd25519PublicKey(ac.contact.Pk) if err != nil { return errcode.ErrCode_ErrDeserialization.Wrap(err) } group, err := m.secretStore.GetGroupForContact(contactPK) if err != nil { return errcode.ErrCode_ErrOrbitDBOpen.Wrap(err) } m.contactsFromGroupPK[string(group.PublicKey)] = ac return nil } func (m *metadataStoreIndex) handleContactRequestOutgoingEnqueued(event proto.Message) error { evt, ok := event.(*protocoltypes.AccountContactRequestOutgoingEnqueued) if ko := !ok || evt.Contact == nil; ko { return errcode.ErrCode_ErrInvalidInput } if _, ok := m.contacts[string(evt.Contact.Pk)]; ok { if m.contacts[string(evt.Contact.Pk)].contact.Metadata == nil { m.contacts[string(evt.Contact.Pk)].contact.Metadata = evt.Contact.Metadata } if m.contacts[string(evt.Contact.Pk)].contact.PublicRendezvousSeed == nil { m.contacts[string(evt.Contact.Pk)].contact.PublicRendezvousSeed = evt.Contact.PublicRendezvousSeed } return nil } if data, ok := m.contactRequestMetadata[string(evt.Contact.Pk)]; !ok || len(data) == 0 { m.contactRequestMetadata[string(evt.Contact.Pk)] = evt.OwnMetadata } ac := &AccountContact{ state: protocoltypes.ContactState_ContactStateToRequest, contact: &protocoltypes.ShareableContact{ Pk: evt.Contact.Pk, Metadata: evt.Contact.Metadata, PublicRendezvousSeed: evt.Contact.PublicRendezvousSeed, }, } m.contacts[string(evt.Contact.Pk)] = ac err := m.registerContactFromGroupPK(ac) return err } func (m *metadataStoreIndex) handleContactRequestOutgoingSent(event proto.Message) error { evt, ok := event.(*protocoltypes.AccountContactRequestOutgoingSent) if !ok { return errcode.ErrCode_ErrInvalidInput } if _, ok := m.contacts[string(evt.ContactPk)]; ok { return nil } ac := &AccountContact{ state: protocoltypes.ContactState_ContactStateAdded, contact: &protocoltypes.ShareableContact{ Pk: evt.ContactPk, }, } m.contacts[string(evt.ContactPk)] = ac err := m.registerContactFromGroupPK(ac) return err } func (m *metadataStoreIndex) handleContactRequestIncomingReceived(event proto.Message) error { evt, ok := event.(*protocoltypes.AccountContactRequestIncomingReceived) if !ok { return errcode.ErrCode_ErrInvalidInput } if _, ok := m.contacts[string(evt.ContactPk)]; ok { if m.contacts[string(evt.ContactPk)].contact.Metadata == nil { m.contacts[string(evt.ContactPk)].contact.Metadata = evt.ContactMetadata } if m.contacts[string(evt.ContactPk)].contact.PublicRendezvousSeed == nil { m.contacts[string(evt.ContactPk)].contact.PublicRendezvousSeed = evt.ContactRendezvousSeed } return nil } ac := &AccountContact{ state: protocoltypes.ContactState_ContactStateReceived, contact: &protocoltypes.ShareableContact{ Pk: evt.ContactPk, Metadata: evt.ContactMetadata, PublicRendezvousSeed: evt.ContactRendezvousSeed, }, } m.contacts[string(evt.ContactPk)] = ac err := m.registerContactFromGroupPK(ac) return err } func (m *metadataStoreIndex) handleContactRequestIncomingDiscarded(event proto.Message) error { evt, ok := event.(*protocoltypes.AccountContactRequestIncomingDiscarded) if !ok { return errcode.ErrCode_ErrInvalidInput } if _, ok := m.contacts[string(evt.ContactPk)]; ok { return nil } ac := &AccountContact{ state: protocoltypes.ContactState_ContactStateDiscarded, contact: &protocoltypes.ShareableContact{ Pk: evt.ContactPk, }, } m.contacts[string(evt.ContactPk)] = ac err := m.registerContactFromGroupPK(ac) return err } func (m *metadataStoreIndex) handleContactRequestIncomingAccepted(event proto.Message) error { evt, ok := event.(*protocoltypes.AccountContactRequestIncomingAccepted) if !ok { return errcode.ErrCode_ErrInvalidInput } if _, ok := m.contacts[string(evt.ContactPk)]; ok { return nil } ac := &AccountContact{ state: protocoltypes.ContactState_ContactStateAdded, contact: &protocoltypes.ShareableContact{ Pk: evt.ContactPk, }, } m.contacts[string(evt.ContactPk)] = ac err := m.registerContactFromGroupPK(ac) return err } func (m *metadataStoreIndex) handleContactBlocked(event proto.Message) error { evt, ok := event.(*protocoltypes.AccountContactBlocked) if !ok { return errcode.ErrCode_ErrInvalidInput } if _, ok := m.contacts[string(evt.ContactPk)]; ok { return nil } ac := &AccountContact{ state: protocoltypes.ContactState_ContactStateBlocked, contact: &protocoltypes.ShareableContact{ Pk: evt.ContactPk, }, } m.contacts[string(evt.ContactPk)] = ac err := m.registerContactFromGroupPK(ac) return err } func (m *metadataStoreIndex) handleContactUnblocked(event proto.Message) error { evt, ok := event.(*protocoltypes.AccountContactUnblocked) if !ok { return errcode.ErrCode_ErrInvalidInput } if _, ok := m.contacts[string(evt.ContactPk)]; ok { return nil } ac := &AccountContact{ state: protocoltypes.ContactState_ContactStateRemoved, contact: &protocoltypes.ShareableContact{ Pk: evt.ContactPk, }, } m.contacts[string(evt.ContactPk)] = ac err := m.registerContactFromGroupPK(ac) return err } func (m *metadataStoreIndex) handleContactAliasKeyAdded(event proto.Message) error { evt, ok := event.(*protocoltypes.ContactAliasKeyAdded) if !ok { return errcode.ErrCode_ErrInvalidInput } m.eventsContactAddAliasKey = append(m.eventsContactAddAliasKey, evt) return nil } func (m *metadataStoreIndex) handleMultiMemberInitialMember(event proto.Message) error { e, ok := event.(*protocoltypes.MultiMemberGroupInitialMemberAnnounced) if !ok { return errcode.ErrCode_ErrInvalidInput } pk, err := crypto.UnmarshalEd25519PublicKey(e.MemberPk) if err != nil { return errcode.ErrCode_ErrDeserialization.Wrap(err) } if _, ok := m.admins[pk]; ok { return errcode.ErrCode_ErrInternal } m.admins[pk] = struct{}{} return nil } //nolint:revive func (m *metadataStoreIndex) handleMultiMemberGrantAdminRole(event proto.Message) error { // TODO: return nil } func (m *metadataStoreIndex) handleGroupMetadataPayloadSent(_ proto.Message) error { return nil } func (m *metadataStoreIndex) handleAccountVerifiedCredentialRegistered(event proto.Message) error { e, ok := event.(*protocoltypes.AccountVerifiedCredentialRegistered) if !ok { return errcode.ErrCode_ErrInvalidInput } m.verifiedCredentials = append(m.verifiedCredentials, e) return nil } func (m *metadataStoreIndex) listAdmins() []crypto.PubKey { m.lock.RLock() defer m.lock.RUnlock() admins := make([]crypto.PubKey, len(m.admins)) i := 0 for admin := range m.admins { admins[i] = admin i++ } return admins } func (m *metadataStoreIndex) listOtherMembersDevices() []crypto.PubKey { m.lock.RLock() defer m.lock.RUnlock() if m.ownMemberDevice == nil || m.ownMemberDevice.Member() == nil { return nil } ownMemberPK, err := m.ownMemberDevice.Member().Raw() if err != nil { m.logger.Warn("unable to serialize member pubkey", zap.Error(err)) return nil } devices := []crypto.PubKey(nil) for pk, devicesForMember := range m.members { if string(ownMemberPK) == pk { continue } for _, md := range devicesForMember { devices = append(devices, md.Device()) } } return devices } func (m *metadataStoreIndex) contactRequestsEnabled() bool { m.lock.RLock() defer m.lock.RUnlock() return m.contactRequestEnabled != nil && *m.contactRequestEnabled } func (m *metadataStoreIndex) contactRequestsSeed() []byte { m.lock.RLock() defer m.lock.RUnlock() return m.contactRequestSeed } func (m *metadataStoreIndex) getContact(pk crypto.PubKey) (*AccountContact, error) { m.lock.RLock() defer m.lock.RUnlock() bytes, err := pk.Raw() if err != nil { return nil, errcode.ErrCode_ErrSerialization.Wrap(err) } contact, ok := m.contacts[string(bytes)] if !ok { return nil, errcode.ErrCode_ErrMissingMapKey.Wrap(err) } return contact, nil } func (m *metadataStoreIndex) postHandlerSentAliases() error { for _, evt := range m.eventsContactAddAliasKey { memberPublicKey, err := m.unsafeGetMemberByDevice(evt.DevicePk) if err != nil { return fmt.Errorf("couldn't get member for device") } if memberPublicKey.Equals(m.ownMemberDevice.Member()) { m.ownAliasKeySent = true continue } if l := len(evt.AliasPk); l != cryptoutil.KeySize { return errcode.ErrCode_ErrInvalidInput.Wrap(fmt.Errorf("invalid alias key size, expected %d, got %d", cryptoutil.KeySize, l)) } m.otherAliasKey = evt.AliasPk } m.eventsContactAddAliasKey = nil return nil } // nolint:staticcheck,revive // newMetadataIndex returns a new index to manage the list of the group members func newMetadataIndex(ctx context.Context, g *protocoltypes.Group, md secretstore.MemberDevice, secretStore secretstore.SecretStore) iface.IndexConstructor { return func(publicKey []byte) iface.StoreIndex { m := &metadataStoreIndex{ members: map[string][]secretstore.MemberDevice{}, devices: map[string]secretstore.MemberDevice{}, admins: map[crypto.PubKey]struct{}{}, sentSecrets: map[string]struct{}{}, handledEvents: map[string]struct{}{}, contacts: map[string]*AccountContact{}, contactsFromGroupPK: map[string]*AccountContact{}, groups: map[string]*accountGroup{}, contactRequestMetadata: map[string][]byte{}, group: g, ownMemberDevice: md, secretStore: secretStore, ctx: ctx, logger: zap.NewNop(), } m.eventHandlers = map[protocoltypes.EventType][]func(event proto.Message) error{ protocoltypes.EventType_EventTypeAccountContactBlocked: {m.handleContactBlocked}, protocoltypes.EventType_EventTypeAccountContactRequestDisabled: {m.handleContactRequestDisabled}, protocoltypes.EventType_EventTypeAccountContactRequestEnabled: {m.handleContactRequestEnabled}, protocoltypes.EventType_EventTypeAccountContactRequestIncomingAccepted: {m.handleContactRequestIncomingAccepted}, protocoltypes.EventType_EventTypeAccountContactRequestIncomingDiscarded: {m.handleContactRequestIncomingDiscarded}, protocoltypes.EventType_EventTypeAccountContactRequestIncomingReceived: {m.handleContactRequestIncomingReceived}, protocoltypes.EventType_EventTypeAccountContactRequestOutgoingEnqueued: {m.handleContactRequestOutgoingEnqueued}, protocoltypes.EventType_EventTypeAccountContactRequestOutgoingSent: {m.handleContactRequestOutgoingSent}, protocoltypes.EventType_EventTypeAccountContactRequestReferenceReset: {m.handleContactRequestReferenceReset}, protocoltypes.EventType_EventTypeAccountContactUnblocked: {m.handleContactUnblocked}, protocoltypes.EventType_EventTypeAccountGroupJoined: {m.handleGroupJoined}, protocoltypes.EventType_EventTypeAccountGroupLeft: {m.handleGroupLeft}, protocoltypes.EventType_EventTypeContactAliasKeyAdded: {m.handleContactAliasKeyAdded}, protocoltypes.EventType_EventTypeGroupDeviceChainKeyAdded: {m.handleGroupDeviceChainKeyAdded}, protocoltypes.EventType_EventTypeGroupMemberDeviceAdded: {m.handleGroupMemberDeviceAdded}, protocoltypes.EventType_EventTypeMultiMemberGroupAdminRoleGranted: {m.handleMultiMemberGrantAdminRole}, protocoltypes.EventType_EventTypeMultiMemberGroupInitialMemberAnnounced: {m.handleMultiMemberInitialMember}, protocoltypes.EventType_EventTypeGroupMetadataPayloadSent: {m.handleGroupMetadataPayloadSent}, protocoltypes.EventType_EventTypeAccountVerifiedCredentialRegistered: {m.handleAccountVerifiedCredentialRegistered}, } m.postIndexActions = []func() error{ m.postHandlerSentAliases, } return m } } var _ iface.StoreIndex = &metadataStoreIndex{} ================================================ FILE: store_metadata_test.go ================================================ package weshnet import ( "bytes" "context" crand "crypto/rand" "fmt" mrand "math/rand" "testing" "time" datastore "github.com/ipfs/go-datastore" ds_sync "github.com/ipfs/go-datastore/sync" "github.com/libp2p/go-libp2p/core/crypto" mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap" "berty.tech/weshnet/v2/pkg/ipfsutil" "berty.tech/weshnet/v2/pkg/protocoltypes" "berty.tech/weshnet/v2/pkg/testutil" ) func TestMetadataStoreSecret_Basic(t *testing.T) { t.Skip("skipping as we don't care about this code now") // TODO: handle more cases (more members, more devices...) memberCount := 2 deviceCount := 1 ctx, cancel := context.WithCancel(context.Background()) defer cancel() peers, groupSK, cleanup := CreatePeersWithGroupTest(ctx, t, "/tmp/secrets_test", memberCount, deviceCount) defer cleanup() secretsAdded := make(chan struct{}) msA := peers[0].GC.MetadataStore() msB := peers[1].GC.MetadataStore() go waitForBertyEventType(ctx, t, msA, protocoltypes.EventType_EventTypeGroupDeviceChainKeyAdded, 2, secretsAdded) go waitForBertyEventType(ctx, t, msB, protocoltypes.EventType_EventTypeGroupDeviceChainKeyAdded, 2, secretsAdded) inviteAllPeersToGroup(ctx, t, peers, groupSK) devPkA := peers[0].GC.DevicePubKey() devPkB := peers[1].GC.DevicePubKey() <-secretsAdded <-secretsAdded _ = devPkA _ = devPkB } func TestMetadataStoreMember(t *testing.T) { t.Skip("skipping as we don't care about this code now") // If seed is not set, it will default to 1, explicitly setting it and displaying it if the test fails seed := time.Now().UTC().UnixNano() mrand.Seed(seed) for _, tc := range []struct { memberCount int deviceCount int slow bool }{ {memberCount: 1, deviceCount: 1, slow: false}, {memberCount: 1, deviceCount: 3, slow: false}, {memberCount: 3, deviceCount: 1, slow: false}, // TODO: fix pubsub issues // {memberCount: 3, deviceCount: 3, slow: false}, } { t.Run(fmt.Sprintf("testMemberStore seed: %d, memberCount: %d, deviceCount: %d", seed, tc.memberCount, tc.deviceCount), func(t *testing.T) { if tc.slow { // TODO: re-enable this test t.Skip() testutil.FilterSpeed(t, testutil.Slow) } testMemberStore(t, tc.memberCount, tc.deviceCount) }) } } func testMemberStore(t *testing.T, memberCount, deviceCount int) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() // Creates N members with M devices each within the same group peers, groupSK, cleanup := CreatePeersWithGroupTest(ctx, t, "/tmp/member_test", memberCount, deviceCount) defer cleanup() done := make(chan struct{}) for _, peer := range peers { go waitForBertyEventType(ctx, t, peer.GC.MetadataStore(), protocoltypes.EventType_EventTypeGroupMemberDeviceAdded, len(peers), done) } for i, peer := range peers { if _, err := peer.GC.MetadataStore().AddDeviceToGroup(ctx); err != nil { t.Fatal(err) } if i == 0 { if _, err := peer.GC.MetadataStore().ClaimGroupOwnership(ctx, groupSK); err != nil { t.Fatal(err) } } } // Wait for all events to be received in all peers's member log (or timeout) for i := range peers { <-done t.Logf("got all members for member %d", i) } // Test if everything was replicated and indexed correctly for i, peer := range peers { ms := peer.GC.MetadataStore() // Test list functions (only length, checking all entries would be too long) memberList := ms.ListMembers() if len(memberList) != memberCount { t.Fatalf("%d member(s) missing from peer %d member list (%d/%d)", memberCount-len(memberList), i, len(memberList), memberCount) } deviceList := ms.ListDevices() if len(deviceList) != memberCount*deviceCount { t.Fatalf("%d device(s) missing from peer %d device list (%d/%d)", memberCount*deviceCount-len(deviceList), i, len(deviceList), memberCount*deviceCount) } // Test entries getter functions for j, peerDev := range peers { if _, err := ms.GetDevicesForMember(peerDev.GC.MemberPubKey()); err != nil { t.Fatalf("member of peer %d is missing from peer %d member map: %v", j, i, err) } if _, err := ms.GetMemberByDevice(peerDev.GC.MemberPubKey()); err != nil { t.Fatalf("device of peer %d is missing from peer %d device map: %v", j, i, err) } } } } func ipfsAPIUsingMockNet(ctx context.Context, t *testing.T) ipfsutil.ExtendedCoreAPI { ipfsopts := &ipfsutil.TestingAPIOpts{ Logger: zap.NewNop(), Mocknet: mocknet.New(), Datastore: ds_sync.MutexWrap(datastore.NewMapDatastore()), } node := ipfsutil.TestingCoreAPIUsingMockNet(ctx, t, ipfsopts) return node.API() } func TestMetadataRendezvousPointLifecycle(t *testing.T) { testutil.FilterSpeed(t, testutil.Fast) ctx, cancel := context.WithCancel(context.Background()) defer cancel() // Creates N members with M devices each within the same group peers, _, cleanup := CreatePeersWithGroupTest(ctx, t, "/tmp/member_test", 1, 1) defer cleanup() api := ipfsAPIUsingMockNet(ctx, t) ownCG, err := peers[0].DB.openAccountGroup(ctx, nil, api) assert.NoError(t, err) meta := ownCG.MetadataStore() _, accountMemberDevice, err := peers[0].SecretStore.GetGroupForAccount() assert.NoError(t, err) accPK, err := accountMemberDevice.Member().Raw() assert.NoError(t, err) enabled, shareableContact := meta.GetIncomingContactRequestsStatus() assert.False(t, enabled) assert.NotNil(t, shareableContact) // reset rdv seed _, err = meta.ContactRequestReferenceReset(ctx) assert.NoError(t, err) // get set shareable contact, not enabled enabled, shareableContact = meta.GetIncomingContactRequestsStatus() assert.False(t, enabled) assert.NotNil(t, shareableContact) assert.Equal(t, accPK, shareableContact.Pk) assert.Equal(t, 32, len(shareableContact.PublicRendezvousSeed)) _, err = meta.ContactRequestEnable(ctx) assert.NoError(t, err) enabled, shareableContact = meta.GetIncomingContactRequestsStatus() assert.True(t, enabled) assert.NotNil(t, shareableContact) // Force reindex to check both enabled and seed err = meta.Load(ctx, -1) assert.NoError(t, err) enabled, shareableContact = meta.GetIncomingContactRequestsStatus() assert.True(t, enabled) assert.NotNil(t, shareableContact) assert.Equal(t, accPK, shareableContact.Pk) assert.Equal(t, 32, len(shareableContact.PublicRendezvousSeed)) // Disable incoming contact requests _, err = meta.ContactRequestDisable(ctx) assert.NoError(t, err) enabled, shareableContact = meta.GetIncomingContactRequestsStatus() assert.False(t, enabled) assert.NotNil(t, shareableContact) assert.Equal(t, accPK, shareableContact.Pk) assert.Equal(t, 32, len(shareableContact.PublicRendezvousSeed)) } func TestMetadataContactLifecycle(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() peersCount := 4 // Creates N members with M devices each within the same group peers, _, cleanup := CreatePeersWithGroupTest(ctx, t, "/tmp/member_test", peersCount, 1) defer cleanup() var ( err error meta = make([]*MetadataStore, peersCount) ownCG = make([]*GroupContext, peersCount) contacts = make([]*protocoltypes.ShareableContact, peersCount) ) api := ipfsAPIUsingMockNet(ctx, t) for i, p := range peers { ownCG[i], err = p.DB.openAccountGroup(ctx, nil, api) require.NoError(t, err) meta[i] = ownCG[i].MetadataStore() _, err = meta[i].ContactRequestReferenceReset(ctx) require.NoError(t, err) _, contacts[i] = meta[i].GetIncomingContactRequestsStatus() require.NotNil(t, contacts[i]) contacts[i].Metadata = []byte(fmt.Sprintf("own meta %d", i)) } _, randPK, err := crypto.GenerateEd25519Key(crand.Reader) require.NoError(t, err) // no contacts require.Equal(t, len(meta[0].Index().(*metadataStoreIndex).contacts), 0) _, err = meta[0].ContactRequestReferenceReset(ctx) require.NoError(t, err) contact2PK := contacts[1].Pk contact2RDVS := contacts[1].PublicRendezvousSeed // Enqueuing outgoing _, err = meta[0].ContactRequestOutgoingEnqueue(ctx, contacts[0], contacts[0].Metadata) require.Error(t, err) _, err = meta[0].ContactRequestOutgoingEnqueue(ctx, contacts[1], contacts[0].Metadata) require.NoError(t, err) _, err = meta[0].ContactRequestOutgoingEnqueue(ctx, contacts[1], contacts[0].Metadata) require.NoError(t, err) require.Equal(t, len(meta[0].Index().(*metadataStoreIndex).contacts), 1) require.NotNil(t, meta[0].Index().(*metadataStoreIndex).contacts[string(contact2PK)]) require.Equal(t, meta[0].Index().(*metadataStoreIndex).contacts[string(contact2PK)].state, protocoltypes.ContactState_ContactStateToRequest) require.Equal(t, meta[0].Index().(*metadataStoreIndex).contacts[string(contact2PK)].contact.Pk, contact2PK) require.Equal(t, meta[0].Index().(*metadataStoreIndex).contacts[string(contact2PK)].contact.PublicRendezvousSeed, contact2RDVS) require.Equal(t, contacts[0].Metadata, meta[0].Index().(*metadataStoreIndex).contactRequestMetadata[string(contact2PK)]) contacts[1].PublicRendezvousSeed = nil _, err = meta[0].ContactRequestOutgoingEnqueue(ctx, contacts[1], contacts[0].Metadata) require.Error(t, err) contacts[1].PublicRendezvousSeed = []byte("too_short") _, err = meta[0].ContactRequestOutgoingEnqueue(ctx, contacts[1], contacts[0].Metadata) require.Error(t, err) contacts[1].Pk = nil contacts[1].PublicRendezvousSeed = contact2RDVS _, err = meta[0].ContactRequestOutgoingEnqueue(ctx, contacts[1], contacts[0].Metadata) require.Error(t, err) contacts[1].Pk = []byte("invalid") _, err = meta[0].ContactRequestOutgoingEnqueue(ctx, contacts[1], contacts[0].Metadata) require.Error(t, err) require.Equal(t, 1, len(meta[0].Index().(*metadataStoreIndex).contacts)) require.NotNil(t, meta[0].Index().(*metadataStoreIndex).contacts[string(contact2PK)]) require.Equal(t, protocoltypes.ContactState_ContactStateToRequest, meta[0].Index().(*metadataStoreIndex).contacts[string(contact2PK)].state) require.Equal(t, contact2PK, meta[0].Index().(*metadataStoreIndex).contacts[string(contact2PK)].contact.Pk) require.Equal(t, contact2RDVS, meta[0].Index().(*metadataStoreIndex).contacts[string(contact2PK)].contact.PublicRendezvousSeed) require.Equal(t, contacts[0].Metadata, meta[0].Index().(*metadataStoreIndex).contactRequestMetadata[string(contact2PK)]) contacts[1].Pk = contact2PK contacts[1].PublicRendezvousSeed = contact2RDVS meta[0].Index().(*metadataStoreIndex).contactRequestMetadata[string(contacts[1].Pk)] = contacts[0].Metadata // Marking as sent _, err = meta[0].ContactRequestOutgoingSent(ctx, nil) require.Error(t, err) _, err = meta[0].ContactRequestOutgoingSent(ctx, randPK) require.Error(t, err) _, err = meta[0].ContactRequestOutgoingSent(ctx, ownCG[0].MemberPubKey()) require.Error(t, err) meta[0].Index().(*metadataStoreIndex).contacts[string(contacts[1].Pk)].state = protocoltypes.ContactState_ContactStateAdded _, err = meta[0].ContactRequestOutgoingSent(ctx, ownCG[1].MemberPubKey()) require.Error(t, err) meta[0].Index().(*metadataStoreIndex).contacts[string(contacts[1].Pk)].state = protocoltypes.ContactState_ContactStateToRequest _, err = meta[0].ContactRequestOutgoingSent(ctx, ownCG[1].MemberPubKey()) require.NoError(t, err) require.Equal(t, len(meta[0].Index().(*metadataStoreIndex).contacts), 1) require.NotNil(t, meta[0].Index().(*metadataStoreIndex).contacts[string(contacts[1].Pk)]) require.Equal(t, protocoltypes.ContactState_ContactStateAdded, meta[0].Index().(*metadataStoreIndex).contacts[string(contacts[1].Pk)].state) require.Equal(t, contacts[1].Pk, meta[0].Index().(*metadataStoreIndex).contacts[string(contacts[1].Pk)].contact.Pk) require.Equal(t, contacts[1].PublicRendezvousSeed, meta[0].Index().(*metadataStoreIndex).contacts[string(contacts[1].Pk)].contact.PublicRendezvousSeed) // Marking as received _, err = meta[1].ContactRequestIncomingReceived(ctx, &protocoltypes.ShareableContact{}) require.Error(t, err) _, err = meta[1].ContactRequestIncomingReceived(ctx, &protocoltypes.ShareableContact{Pk: []byte("invalid"), PublicRendezvousSeed: []byte("invalid")}) require.Error(t, err) _, err = meta[1].ContactRequestIncomingReceived(ctx, &protocoltypes.ShareableContact{Pk: []byte("invalid"), PublicRendezvousSeed: contacts[0].PublicRendezvousSeed}) require.Error(t, err) _, err = meta[1].ContactRequestIncomingReceived(ctx, &protocoltypes.ShareableContact{Pk: contacts[0].Pk, PublicRendezvousSeed: []byte("invalid")}) require.Error(t, err) _, err = meta[1].ContactRequestIncomingReceived(ctx, contacts[1]) require.Error(t, err) _, err = meta[1].ContactRequestIncomingReceived(ctx, contacts[0]) require.NoError(t, err) require.Equal(t, len(meta[1].Index().(*metadataStoreIndex).contacts), 1) require.NotNil(t, meta[1].Index().(*metadataStoreIndex).contacts[string(contacts[0].Pk)]) require.Equal(t, protocoltypes.ContactState_ContactStateReceived, meta[1].Index().(*metadataStoreIndex).contacts[string(contacts[0].Pk)].state) require.Equal(t, contacts[0].Pk, meta[1].Index().(*metadataStoreIndex).contacts[string(contacts[0].Pk)].contact.Pk) require.Equal(t, contacts[0].PublicRendezvousSeed, meta[1].Index().(*metadataStoreIndex).contacts[string(contacts[0].Pk)].contact.PublicRendezvousSeed) require.Equal(t, contacts[0].Metadata, meta[1].Index().(*metadataStoreIndex).contacts[string(contacts[0].Pk)].contact.Metadata) // Accepting received _, err = meta[1].ContactRequestIncomingAccept(ctx, nil) require.Error(t, err) _, err = meta[1].ContactRequestIncomingAccept(ctx, randPK) require.Error(t, err) _, err = meta[1].ContactRequestIncomingAccept(ctx, ownCG[1].MemberPubKey()) require.Error(t, err) _, err = meta[1].ContactRequestIncomingAccept(ctx, ownCG[0].MemberPubKey()) require.NoError(t, err) require.Equal(t, len(meta[1].Index().(*metadataStoreIndex).contacts), 1) require.NotNil(t, meta[1].Index().(*metadataStoreIndex).contacts[string(contacts[0].Pk)]) require.Equal(t, protocoltypes.ContactState_ContactStateAdded, meta[1].Index().(*metadataStoreIndex).contacts[string(contacts[0].Pk)].state) require.Equal(t, contacts[0].Pk, meta[1].Index().(*metadataStoreIndex).contacts[string(contacts[0].Pk)].contact.Pk) require.Equal(t, contacts[0].PublicRendezvousSeed, meta[1].Index().(*metadataStoreIndex).contacts[string(contacts[0].Pk)].contact.PublicRendezvousSeed) _, err = meta[1].ContactRequestIncomingReceived(ctx, contacts[0]) require.Error(t, err) // Auto accept when receiving an invitation from a contact you were waiting to send an invitation to _, err = meta[1].ContactRequestOutgoingEnqueue(ctx, contacts[3], contacts[1].Metadata) require.NoError(t, err) _, err = meta[1].ContactRequestIncomingReceived(ctx, contacts[3]) require.NoError(t, err) require.Equal(t, 2, len(meta[1].Index().(*metadataStoreIndex).contacts)) require.Equal(t, meta[1].Index().(*metadataStoreIndex).contacts[string(contacts[3].Pk)].state, protocoltypes.ContactState_ContactStateAdded) // Refuse contact _, err = meta[1].ContactRequestIncomingReceived(ctx, contacts[2]) require.NoError(t, err) require.Equal(t, len(meta[1].Index().(*metadataStoreIndex).contacts), 3) require.Equal(t, meta[1].Index().(*metadataStoreIndex).contacts[string(contacts[2].Pk)].state, protocoltypes.ContactState_ContactStateReceived) _, err = meta[1].ContactRequestIncomingDiscard(ctx, nil) require.Error(t, err) _, err = meta[1].ContactRequestIncomingDiscard(ctx, randPK) require.Error(t, err) _, err = meta[1].ContactRequestIncomingDiscard(ctx, ownCG[0].MemberPubKey()) require.Error(t, err) _, err = meta[1].ContactRequestIncomingDiscard(ctx, ownCG[1].MemberPubKey()) require.Error(t, err) _, err = meta[1].ContactRequestIncomingDiscard(ctx, ownCG[2].MemberPubKey()) require.NoError(t, err) require.Equal(t, len(meta[1].Index().(*metadataStoreIndex).contacts), 3) require.Equal(t, meta[1].Index().(*metadataStoreIndex).contacts[string(contacts[2].Pk)].state, protocoltypes.ContactState_ContactStateDiscarded) // Allow receiving requests again after discarded _, err = meta[1].ContactRequestIncomingReceived(ctx, contacts[2]) require.NoError(t, err) require.Equal(t, len(meta[1].Index().(*metadataStoreIndex).contacts), 3) require.Equal(t, meta[1].Index().(*metadataStoreIndex).contacts[string(contacts[2].Pk)].state, protocoltypes.ContactState_ContactStateReceived) _, err = meta[1].ContactRequestOutgoingEnqueue(ctx, contacts[2], contacts[1].Metadata) require.NoError(t, err) require.Equal(t, len(meta[1].Index().(*metadataStoreIndex).contacts), 3) require.Equal(t, meta[1].Index().(*metadataStoreIndex).contacts[string(contacts[2].Pk)].state, protocoltypes.ContactState_ContactStateAdded) // Auto accept discarded requests meta[1].Index().(*metadataStoreIndex).contacts[string(contacts[2].Pk)].state = protocoltypes.ContactState_ContactStateDiscarded _, err = meta[1].ContactRequestOutgoingEnqueue(ctx, contacts[2], contacts[1].Metadata) require.NoError(t, err) require.Equal(t, len(meta[1].Index().(*metadataStoreIndex).contacts), 3) require.Equal(t, meta[1].Index().(*metadataStoreIndex).contacts[string(contacts[2].Pk)].state, protocoltypes.ContactState_ContactStateAdded) // Block contact require.Equal(t, len(meta[2].Index().(*metadataStoreIndex).contacts), 0) _, err = meta[2].ContactBlock(ctx, ownCG[2].MemberPubKey()) require.Error(t, err) require.Equal(t, len(meta[2].Index().(*metadataStoreIndex).contacts), 0) _, err = meta[2].ContactBlock(ctx, ownCG[0].MemberPubKey()) require.NoError(t, err) require.Equal(t, len(meta[2].Index().(*metadataStoreIndex).contacts), 1) require.Equal(t, meta[2].Index().(*metadataStoreIndex).contacts[string(contacts[0].Pk)].state, protocoltypes.ContactState_ContactStateBlocked) _, err = meta[2].ContactBlock(ctx, ownCG[0].MemberPubKey()) require.Error(t, err) require.Equal(t, len(meta[2].Index().(*metadataStoreIndex).contacts), 1) require.Equal(t, meta[2].Index().(*metadataStoreIndex).contacts[string(contacts[0].Pk)].state, protocoltypes.ContactState_ContactStateBlocked) // Unblock contact _, err = meta[2].ContactUnblock(ctx, ownCG[1].MemberPubKey()) require.Error(t, err) require.Equal(t, len(meta[2].Index().(*metadataStoreIndex).contacts), 1) require.Equal(t, meta[2].Index().(*metadataStoreIndex).contacts[string(contacts[0].Pk)].state, protocoltypes.ContactState_ContactStateBlocked) _, err = meta[2].ContactUnblock(ctx, ownCG[0].MemberPubKey()) require.NoError(t, err) require.Equal(t, len(meta[2].Index().(*metadataStoreIndex).contacts), 1) require.Equal(t, meta[2].Index().(*metadataStoreIndex).contacts[string(contacts[0].Pk)].state, protocoltypes.ContactState_ContactStateRemoved) _, err = meta[2].ContactUnblock(ctx, ownCG[0].MemberPubKey()) require.Error(t, err) require.Equal(t, len(meta[2].Index().(*metadataStoreIndex).contacts), 1) require.Equal(t, meta[2].Index().(*metadataStoreIndex).contacts[string(contacts[0].Pk)].state, protocoltypes.ContactState_ContactStateRemoved) } func TestMetadataAliasLifecycle(t *testing.T) { testutil.FilterSpeed(t, testutil.Slow) ctx, cancel := context.WithCancel(context.Background()) defer cancel() peersCount := 4 // Creates N members with M devices each within the same group peers, _, cleanup := CreatePeersWithGroupTest(ctx, t, "/tmp/member_test", peersCount, 1) defer cleanup() // disclose _, err := peers[0].GC.MetadataStore().ContactSendAliasKey(ctx) require.Error(t, err) g, err := peers[0].SecretStore.GetGroupForContact(peers[1].GC.MemberPubKey()) require.NoError(t, err) cg0, err := peers[0].DB.OpenGroup(ctx, g, nil) require.NoError(t, err) defer cg0.Close() require.False(t, cg0.MetadataStore().Index().(*metadataStoreIndex).ownAliasKeySent) _, err = cg0.MetadataStore().AddDeviceToGroup(ctx) require.NoError(t, err) _, err = cg0.MetadataStore().ContactSendAliasKey(ctx) require.NoError(t, err) require.Empty(t, cg0.MetadataStore().Index().(*metadataStoreIndex).otherAliasKey) require.True(t, cg0.MetadataStore().Index().(*metadataStoreIndex).ownAliasKeySent) g, err = peers[1].SecretStore.GetGroupForContact(peers[0].GC.MemberPubKey()) require.NoError(t, err) cg1, err := peers[1].DB.OpenGroup(ctx, g, nil) require.NoError(t, err) defer cg1.Close() _ = cg1 // TODO: receive key on cg1 from cg0 // TODO: match received alias proof with previously disclosed key } func TestMetadataGroupsLifecycle(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() // Creates N members with M devices each within the same group peers, _, cleanup := CreatePeersWithGroupTest(ctx, t, "/tmp/member_test", 1, 1) defer cleanup() api := ipfsAPIUsingMockNet(ctx, t) ownCG, err := peers[0].DB.openAccountGroup(ctx, nil, api) assert.NoError(t, err) g1, _, err := NewGroupMultiMember() require.NoError(t, err) g2, _, err := NewGroupMultiMember() require.NoError(t, err) g3, _, err := NewGroupMultiMember() require.NoError(t, err) g1PK, err := g1.GetPubKey() require.NoError(t, err) g3PK, err := g3.GetPubKey() require.NoError(t, err) _, err = ownCG.MetadataStore().GroupJoin(ctx, g1) require.NoError(t, err) groups := ownCG.MetadataStore().ListMultiMemberGroups() require.Len(t, groups, 1) require.Equal(t, groups[0].PublicKey, g1.PublicKey) require.Equal(t, groups[0].Secret, g1.Secret) require.Equal(t, groups[0].SecretSig, g1.SecretSig) require.Equal(t, groups[0].GroupType, g1.GroupType) _, err = ownCG.MetadataStore().GroupJoin(ctx, g2) require.NoError(t, err) groups = ownCG.MetadataStore().ListMultiMemberGroups() require.Len(t, groups, 2) first, second := 0, 1 if bytes.Compare(groups[1].PublicKey, g1.PublicKey) == 0 { first, second = 1, 0 } require.Equal(t, groups[first].PublicKey, g1.PublicKey) require.Equal(t, groups[first].Secret, g1.Secret) require.Equal(t, groups[first].SecretSig, g1.SecretSig) require.Equal(t, groups[first].GroupType, g1.GroupType) require.Equal(t, groups[second].PublicKey, g2.PublicKey) require.Equal(t, groups[second].Secret, g2.Secret) require.Equal(t, groups[second].SecretSig, g2.SecretSig) require.Equal(t, groups[second].GroupType, g2.GroupType) _, err = ownCG.MetadataStore().GroupJoin(ctx, &protocoltypes.Group{ PublicKey: []byte("invalid_pk"), Secret: g3.Secret, SecretSig: g3.SecretSig, GroupType: protocoltypes.GroupType_GroupTypeMultiMember, }) require.Error(t, err) groups = ownCG.MetadataStore().ListMultiMemberGroups() require.Len(t, groups, 2) _, err = ownCG.MetadataStore().GroupJoin(ctx, &protocoltypes.Group{ PublicKey: g3.PublicKey, Secret: nil, SecretSig: g3.SecretSig, GroupType: protocoltypes.GroupType_GroupTypeMultiMember, }) require.Error(t, err) _, err = ownCG.MetadataStore().GroupJoin(ctx, &protocoltypes.Group{ PublicKey: g3.PublicKey, Secret: g3.Secret, SecretSig: []byte("invalid_sig"), GroupType: protocoltypes.GroupType_GroupTypeMultiMember, }) require.Error(t, err) _, err = ownCG.MetadataStore().GroupJoin(ctx, g1) require.Error(t, err) groups = ownCG.MetadataStore().ListMultiMemberGroups() require.Len(t, groups, 2) _, err = ownCG.MetadataStore().GroupLeave(ctx, nil) require.Error(t, err) groups = ownCG.MetadataStore().ListMultiMemberGroups() require.Len(t, groups, 2) _, err = ownCG.MetadataStore().GroupLeave(ctx, g1PK) require.NoError(t, err) groups = ownCG.MetadataStore().ListMultiMemberGroups() require.Len(t, groups, 1) _, err = ownCG.MetadataStore().GroupLeave(ctx, g1PK) require.Error(t, err) groups = ownCG.MetadataStore().ListMultiMemberGroups() require.Len(t, groups, 1) _, err = ownCG.MetadataStore().GroupLeave(ctx, g3PK) require.Error(t, err) groups = ownCG.MetadataStore().ListMultiMemberGroups() require.Len(t, groups, 1) require.Equal(t, groups[0].PublicKey, g2.PublicKey) require.Equal(t, groups[0].Secret, g2.Secret) require.Equal(t, groups[0].SecretSig, g2.SecretSig) require.Equal(t, groups[0].GroupType, g2.GroupType) } func TestFlappyMultiDevices_Basic(t *testing.T) { testutil.FilterStabilityAndSpeed(t, testutil.Flappy, testutil.Slow) memberCount := 2 deviceCount := 3 totalDevices := memberCount * deviceCount ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() peers, _, cleanup := CreatePeersWithGroupTest(ctx, t, "/tmp/multidevices_test", memberCount, deviceCount) defer cleanup() api := ipfsAPIUsingMockNet(ctx, t) // make peer index pi := [][]int{} for i := 0; i < memberCount; i++ { pi = append(pi, []int{}) for j := 0; j < deviceCount; j++ { pi[i] = append(pi[i], i*deviceCount+j) } } var ( err error meta = make([]*MetadataStore, totalDevices) ownCG = make([]*GroupContext, totalDevices) contacts = make([]*protocoltypes.ShareableContact, totalDevices) listContacts map[string]*AccountContact groups []*protocoltypes.Group ) // Activate account group + contact request for i, p := range peers { // except for the latest peer devices if (i % deviceCount) == (deviceCount - 1) { continue } ownCG[i], err = p.DB.openAccountGroup(ctx, nil, api) require.NoError(t, err) meta[i] = ownCG[i].MetadataStore() _, err = meta[i].ContactRequestEnable(ctx) assert.NoError(t, err) _, err = meta[i].ContactRequestReferenceReset(ctx) require.NoError(t, err) _, contacts[i] = meta[i].GetIncomingContactRequestsStatus() require.NotNil(t, contacts[i]) contacts[i].Metadata = []byte(fmt.Sprintf("own meta %d", i)) } syncChan := make(chan struct{}) go waitForBertyEventType(ctx, t, meta[pi[0][0]], protocoltypes.EventType_EventTypeAccountContactRequestOutgoingEnqueued, 1, syncChan) go waitForBertyEventType(ctx, t, meta[pi[0][1]], protocoltypes.EventType_EventTypeAccountContactRequestOutgoingEnqueued, 1, syncChan) go waitForBertyEventType(ctx, t, meta[pi[1][0]], protocoltypes.EventType_EventTypeAccountContactRequestIncomingReceived, 1, syncChan) go waitForBertyEventType(ctx, t, meta[pi[1][1]], protocoltypes.EventType_EventTypeAccountContactRequestIncomingReceived, 1, syncChan) // Add peers to contact // Enqueuing outgoing _, err = meta[pi[0][0]].ContactRequestOutgoingEnqueue(ctx, contacts[pi[1][0]], contacts[pi[0][0]].Metadata) require.NoError(t, err) // Marking as sent _, err = meta[pi[0][0]].ContactRequestOutgoingSent(ctx, ownCG[pi[1][0]].MemberPubKey()) require.NoError(t, err) // Marking as received _, err = meta[pi[1][0]].ContactRequestIncomingReceived(ctx, contacts[pi[0][0]]) require.NoError(t, err) // Accepting received _, err = meta[pi[1][0]].ContactRequestIncomingAccept(ctx, ownCG[pi[0][0]].MemberPubKey()) require.NoError(t, err) for i := 0; i < 4; i++ { select { case <-syncChan: case <-ctx.Done(): require.NoError(t, ctx.Err()) } } // test if contact is established listContacts = meta[pi[0][0]].ListContacts() require.Equal(t, 1, len(listContacts)) require.NotNil(t, listContacts[string(contacts[pi[1][0]].Pk)]) listContacts = meta[pi[1][0]].ListContacts() require.Equal(t, 1, len(listContacts)) require.NotNil(t, listContacts[string(contacts[pi[0][0]].Pk)]) // test if 2nd devices have also the contact listContacts = meta[pi[0][1]].ListContacts() require.Equal(t, 1, len(listContacts)) require.NotNil(t, listContacts[string(contacts[pi[1][0]].Pk)]) listContacts = meta[pi[1][1]].ListContacts() require.Equal(t, 1, len(listContacts)) require.NotNil(t, listContacts[string(contacts[pi[0][0]].Pk)]) // Activate group for 2nd peer's 1st device groups = meta[pi[1][0]].ListMultiMemberGroups() require.Len(t, groups, 0) go waitForBertyEventType(ctx, t, meta[pi[1][0]], protocoltypes.EventType_EventTypeAccountGroupJoined, 1, syncChan) go waitForBertyEventType(ctx, t, meta[pi[1][1]], protocoltypes.EventType_EventTypeAccountGroupJoined, 1, syncChan) _, err = meta[pi[1][0]].GroupJoin(ctx, peers[pi[1][0]].GC.group) require.NoError(t, err) for i := 0; i < 2; i++ { select { case <-syncChan: case <-ctx.Done(): require.NoError(t, ctx.Err()) } } groups = meta[pi[1][0]].ListMultiMemberGroups() require.Len(t, groups, 1) // Test if other devices have the group too groups = meta[pi[1][1]].ListMultiMemberGroups() require.Len(t, groups, 1) // Check if a account group activate after the contact request is synchronized // Activate the 2nd peer's latest device account group ownCG[pi[1][2]], err = peers[pi[1][2]].DB.openAccountGroup(ctx, nil, api) require.NoError(t, err) meta[pi[1][2]] = ownCG[pi[1][2]].MetadataStore() // wait for replication db <-time.After(time.Second * 2) listContacts = meta[pi[1][2]].ListContacts() require.Equal(t, 1, len(listContacts)) require.NotNil(t, listContacts[string(contacts[0].Pk)]) // Test for group groups = meta[pi[1][2]].ListMultiMemberGroups() require.Len(t, groups, 1) } ================================================ FILE: store_options.go ================================================ package weshnet import ( "context" "encoding/hex" "github.com/libp2p/go-libp2p/p2p/host/eventbus" "berty.tech/go-ipfs-log/identityprovider" orbitdb "berty.tech/go-orbit-db" "berty.tech/go-orbit-db/accesscontroller" "berty.tech/weshnet/v2/pkg/errcode" "berty.tech/weshnet/v2/pkg/protocoltypes" ) func DefaultOrbitDBOptions(g *protocoltypes.Group, options *orbitdb.CreateDBOptions, keystore *BertySignedKeyStore, storeType string, groupOpenMode GroupOpenMode) (*orbitdb.CreateDBOptions, error) { var err error if options == nil { options = &orbitdb.CreateDBOptions{} } options = &orbitdb.CreateDBOptions{ Directory: options.Directory, Overwrite: options.Overwrite, LocalOnly: options.LocalOnly, StoreType: options.StoreType, AccessControllerAddress: options.AccessControllerAddress, AccessController: options.AccessController, Replicate: options.Replicate, Cache: options.Cache, EventBus: options.EventBus, Logger: options.Logger, } t := true options.Create = &t if options.EventBus == nil { options.EventBus = eventbus.NewBus() } if options.AccessController == nil { options.AccessController, err = defaultACForGroup(g, storeType) if err != nil { return nil, errcode.ErrCode_TODO.Wrap(err) } } options.Keystore = keystore if groupOpenMode != GroupOpenModeReplicate { options.Identity, err = defaultIdentityForGroup(context.TODO(), g, keystore) if err != nil { return nil, errcode.ErrCode_TODO.Wrap(err) } } else { options.Identity, err = readIdentityForGroup(g, keystore) if err != nil { return nil, errcode.ErrCode_TODO.Wrap(err) } } return options, nil } func defaultACForGroup(g *protocoltypes.Group, storeType string) (accesscontroller.ManifestParams, error) { groupID := g.GroupIDAsString() sigPK, err := g.GetSigningPubKey() if err != nil { return nil, errcode.ErrCode_TODO.Wrap(err) } signingKeyBytes, err := sigPK.Raw() if err != nil { return nil, errcode.ErrCode_TODO.Wrap(err) } access := map[string][]string{ "write": {hex.EncodeToString(signingKeyBytes)}, identityGroupIDKey: {groupID}, storeTypeKey: {storeType}, } address, err := simpleAccessControllerCID(access) if err != nil { return nil, err } param := &accesscontroller.CreateAccessControllerOptions{ Access: access, SkipManifest: true, Type: "bertysimple", Address: address, } return param, nil } func defaultIdentityForGroup(ctx context.Context, g *protocoltypes.Group, ks *BertySignedKeyStore) (*identityprovider.Identity, error) { sigPK, err := g.GetSigningPubKey() if err != nil { return nil, errcode.ErrCode_TODO.Wrap(err) } signingKeyBytes, err := sigPK.Raw() if err != nil { return nil, errcode.ErrCode_TODO.Wrap(err) } identity, err := ks.getIdentityProvider().createIdentity(ctx, &identityprovider.CreateIdentityOptions{ Type: identityType, Keystore: ks, ID: hex.EncodeToString(signingKeyBytes), }) if err != nil { return nil, errcode.ErrCode_TODO.Wrap(err) } return identity, nil } func readIdentityForGroup(g *protocoltypes.Group, ks *BertySignedKeyStore) (*identityprovider.Identity, error) { sigPK, err := g.GetSigningPubKey() if err != nil { return nil, errcode.ErrCode_TODO.Wrap(err) } signingKeyBytes, err := sigPK.Raw() if err != nil { return nil, errcode.ErrCode_TODO.Wrap(err) } return &identityprovider.Identity{ ID: hex.EncodeToString(signingKeyBytes), PublicKey: g.PublicKey, Signatures: &identityprovider.IdentitySignature{}, Type: ks.getIdentityProvider().GetType(), Provider: ks.getIdentityProvider(), }, nil } ================================================ FILE: store_utils.go ================================================ package weshnet import ( "bytes" "errors" ipliface "berty.tech/go-ipfs-log/iface" "berty.tech/weshnet/v2/pkg/errcode" ) func getEntriesInRange(entries []ipliface.IPFSLogEntry, since, until []byte) ([]ipliface.IPFSLogEntry, error) { var ( startIndex, stopIndex int startFound, stopFound bool ) if since == nil { startFound = true startIndex = 0 } if until == nil { stopFound = true stopIndex = len(entries) - 1 } for i, entry := range entries { if startFound && stopFound { break } if !startFound && bytes.Equal(entry.GetHash().Bytes(), since) { startFound = true startIndex = i } if !stopFound && bytes.Equal(entry.GetHash().Bytes(), until) { stopFound = true stopIndex = i } } if !startFound { return nil, errcode.ErrCode_ErrInvalidRange.Wrap(errors.New("since ID not found")) } if !stopFound { return nil, errcode.ErrCode_ErrInvalidRange.Wrap(errors.New("until ID not found")) } if startIndex > stopIndex && len(entries) > 0 { return nil, errcode.ErrCode_ErrInvalidRange.Wrap(errors.New("since ID is after until ID")) } return entries[startIndex : stopIndex+1], nil } func iterateOverEntries(entries []ipliface.IPFSLogEntry, reverse bool, f func(ipliface.IPFSLogEntry)) { if reverse { for i := len(entries) - 1; i > -1; i-- { f(entries[i]) } } else { for _, entry := range entries { f(entry) } } } ================================================ FILE: testing.go ================================================ package weshnet import ( "bytes" "context" "fmt" "io" "os" "path/filepath" "sync" "sync/atomic" "testing" "time" grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware" grpc_zap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap" grpc_ctxtags "github.com/grpc-ecosystem/go-grpc-middleware/tags" datastore "github.com/ipfs/go-datastore" ds_sync "github.com/ipfs/go-datastore/sync" "github.com/libp2p/go-libp2p/core/crypto" "github.com/libp2p/go-libp2p/core/peer" mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap" "google.golang.org/grpc" "google.golang.org/protobuf/proto" encrepo "berty.tech/go-ipfs-repo-encrypted" orbitdb "berty.tech/go-orbit-db" "berty.tech/go-orbit-db/pubsub/pubsubraw" "berty.tech/weshnet/v2/internal/datastoreutil" "berty.tech/weshnet/v2/pkg/errcode" "berty.tech/weshnet/v2/pkg/ipfsutil" "berty.tech/weshnet/v2/pkg/protocoltypes" "berty.tech/weshnet/v2/pkg/secretstore" "berty.tech/weshnet/v2/pkg/testutil" "berty.tech/weshnet/v2/pkg/tinder" ) const InMemoryDir = ":memory:" func NewTestOrbitDB(ctx context.Context, t *testing.T, logger *zap.Logger, node ipfsutil.CoreAPIMock, baseDS datastore.Batching) *WeshOrbitDB { t.Helper() api := node.API() selfKey, err := api.Key().Self(ctx) if err != nil { t.Fatal(err) } baseDS = datastoreutil.NewNamespacedDatastore(baseDS, datastore.NewKey(selfKey.ID().String())) secretStore, err := secretstore.NewSecretStore(baseDS, nil) require.NoError(t, err) t.Cleanup(func() { _ = secretStore.Close() }) pubSub := pubsubraw.NewPubSub(node.PubSub(), selfKey.ID(), logger, nil) odb, err := NewWeshOrbitDB(ctx, api, &NewOrbitDBOptions{ Datastore: baseDS, SecretStore: secretStore, NewOrbitDBOptions: orbitdb.NewOrbitDBOptions{ Logger: logger, PubSub: pubSub, }, }) require.NoError(t, err) return odb } type mockedPeer struct { CoreAPI ipfsutil.CoreAPIMock DB *WeshOrbitDB GC *GroupContext SecretStore secretstore.SecretStore } func (m *mockedPeer) PeerInfo() peer.AddrInfo { return m.CoreAPI.MockNode().Peerstore.PeerInfo(m.CoreAPI.MockNode().Identity) } type TestingProtocol struct { Opts *Opts Service Service Client ServiceClient RootDatastore datastore.Batching IpfsCoreAPI ipfsutil.ExtendedCoreAPI OrbitDB *WeshOrbitDB SecretStore secretstore.SecretStore } type TestingOpts struct { Logger *zap.Logger Mocknet mocknet.Mocknet DiscoveryServer *tinder.MockDriverServer SecretStore secretstore.SecretStore CoreAPIMock ipfsutil.CoreAPIMock OrbitDB *WeshOrbitDB ConnectFunc ConnectTestingProtocolFunc } func NewTestingProtocol(ctx context.Context, t testing.TB, opts *TestingOpts, ds datastore.Batching) (*TestingProtocol, func()) { if opts == nil { opts = &TestingOpts{} } opts.applyDefaults(ctx, t) if ds == nil { ds = ds_sync.MutexWrap(datastore.NewMapDatastore()) } ipfsopts := &ipfsutil.TestingAPIOpts{ Logger: opts.Logger, Mocknet: opts.Mocknet, DiscoveryServer: opts.DiscoveryServer, Datastore: ds, } node := opts.CoreAPIMock if node == nil { node = ipfsutil.TestingCoreAPIUsingMockNet(ctx, t, ipfsopts) } secretStore := opts.SecretStore if secretStore == nil { var err error secretStore, err = secretstore.NewInMemSecretStore(&secretstore.NewSecretStoreOptions{}) require.NoError(t, err) } odb := opts.OrbitDB if odb == nil { var err error pubSub := pubsubraw.NewPubSub(node.PubSub(), node.MockNode().PeerHost.ID(), opts.Logger, nil) odb, err = NewWeshOrbitDB(ctx, node.API(), &NewOrbitDBOptions{ NewOrbitDBOptions: orbitdb.NewOrbitDBOptions{ PubSub: pubSub, Logger: opts.Logger, }, Datastore: ds, SecretStore: secretStore, }) require.NoError(t, err) } serviceOpts := Opts{ Host: node.MockNode().PeerHost, PubSub: node.PubSub(), Logger: opts.Logger, RootDatastore: ds, IpfsCoreAPI: node.API(), OrbitDB: odb, TinderService: node.Tinder(), SecretStore: secretStore, } service, cleanupService := TestingService(ctx, t, serviceOpts) // setup client grpcLogger := opts.Logger.Named("grpc") zapOpts := []grpc_zap.Option{} serverOpts := []grpc.ServerOption{ grpc_middleware.WithUnaryServerChain( grpc_ctxtags.UnaryServerInterceptor(grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.CodeGenRequestFieldExtractor)), grpc_zap.UnaryServerInterceptor(grpcLogger, zapOpts...), ), grpc_middleware.WithStreamServerChain( grpc_ctxtags.StreamServerInterceptor(grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.CodeGenRequestFieldExtractor)), grpc_zap.StreamServerInterceptor(grpcLogger, zapOpts...), ), } clientOpts := []grpc.DialOption{ grpc.WithChainUnaryInterceptor(), grpc.WithChainStreamInterceptor(), } server := grpc.NewServer(serverOpts...) client, cleanupClient := TestingClientFromServer(ctx, t, server, service, clientOpts...) tp := &TestingProtocol{ Opts: &serviceOpts, Client: client, Service: service, RootDatastore: ds, IpfsCoreAPI: node.API(), OrbitDB: odb, SecretStore: secretStore, } cleanup := func() { server.Stop() cleanupClient() cleanupService() } return tp, cleanup } func (opts *TestingOpts) applyDefaults(ctx context.Context, t testing.TB) { if opts.Logger == nil { opts.Logger = zap.NewNop() } if opts.Mocknet == nil { opts.Mocknet = mocknet.New() t.Cleanup(func() { opts.Mocknet.Close() }) } if opts.ConnectFunc == nil { opts.ConnectFunc = ConnectAll } } func NewTestingProtocolWithMockedPeers(ctx context.Context, t testing.TB, opts *TestingOpts, ds datastore.Batching, amount int) ([]*TestingProtocol, func()) { t.Helper() opts.applyDefaults(ctx, t) logger := opts.Logger if ds == nil { ds = ds_sync.MutexWrap(datastore.NewMapDatastore()) } if opts.DiscoveryServer == nil { opts.DiscoveryServer = tinder.NewMockDriverServer() } cls := make([]func(), amount) tps := make([]*TestingProtocol, amount) for i := range tps { svcName := fmt.Sprintf("mock%d", i) opts.Logger = logger.Named(svcName) ds := datastoreutil.NewNamespacedDatastore(ds, datastore.NewKey(fmt.Sprintf("%d", i))) tps[i], cls[i] = NewTestingProtocol(ctx, t, opts, ds) } opts.ConnectFunc(t, opts.Mocknet) cleanup := func() { for i := range cls { cls[i]() } } return tps, cleanup } // TestingService returns a configured Client struct with in-memory contexts. func TestingService(ctx context.Context, t testing.TB, opts Opts) (Service, func()) { t.Helper() if opts.Logger == nil { opts.Logger = zap.NewNop() } if opts.IpfsCoreAPI == nil { var mn ipfsutil.CoreAPIMock mn = ipfsutil.TestingCoreAPI(ctx, t) opts.IpfsCoreAPI = mn.API() } service, err := NewService(opts) if err != nil { t.Fatalf("failed to initialize client: %v", err) } cleanup := func() { service.Close() } return service, cleanup } func TestingClientFromServer(ctx context.Context, t testing.TB, s *grpc.Server, svc Service, dialOpts ...grpc.DialOption) (client ServiceClient, cleanup func()) { t.Helper() var err error client, err = NewClientFromService(ctx, s, svc, dialOpts...) require.NoError(t, err) cleanup = func() { client.Close() } return } func TestingClient(ctx context.Context, t testing.TB, svc Service, clientOpts []grpc.DialOption, serverOpts []grpc.ServerOption) (client ServiceClient, cleanup func()) { t.Helper() var err error srv := grpc.NewServer(serverOpts...) client, err = NewClientFromService(ctx, srv, svc, clientOpts...) require.NoError(t, err) cleanup = func() { srv.Stop() client.Close() } return } // Connect Peers Helper type ConnectTestingProtocolFunc func(testing.TB, mocknet.Mocknet) // ConnectAll peers between themselves func ConnectAll(t testing.TB, m mocknet.Mocknet) { t.Helper() err := m.LinkAll() require.NoError(t, err) err = m.ConnectAllButSelf() require.NoError(t, err) } // ConnectInLine, connect peers one by one in order to make a straight line: // ┌───┐ ┌───┐ ┌───┐ ┌───┐ // │ 1 │───▶│ 2 │───▶│ 3 │─ ─ ─ ─ ▶│ x │ // └───┘ └───┘ └───┘ └───┘ func ConnectInLine(t testing.TB, m mocknet.Mocknet) { t.Helper() peers := m.Peers() for i := 0; i < len(peers)-1; i++ { _, err := m.LinkPeers(peers[i], peers[i+1]) require.NoError(t, err) _, err = m.ConnectPeers(peers[i], peers[i+1]) require.NoError(t, err) } } func CreatePeersWithGroupTest(ctx context.Context, t testing.TB, pathBase string, memberCount int, deviceCount int) ([]*mockedPeer, crypto.PrivKey, func()) { t.Helper() logger, cleanupLogger := testutil.Logger(t) var secretStore secretstore.SecretStore mockedPeers := make([]*mockedPeer, memberCount*deviceCount) group, groupPrivateKey, err := NewGroupMultiMember() if err != nil { t.Fatal(err) } mn := mocknet.New() t.Cleanup(func() { mn.Close() }) ipfsopts := ipfsutil.TestingAPIOpts{ Logger: logger, Mocknet: mn, DiscoveryServer: tinder.NewMockDriverServer(), } deviceIndex := 0 cls := make([]func(), memberCount) for i := 0; i < memberCount; i++ { for j := 0; j < deviceCount; j++ { ca := ipfsutil.TestingCoreAPIUsingMockNet(ctx, t, &ipfsopts) if j == 0 { secretStore, err = secretstore.NewInMemSecretStore(nil) require.NoError(t, err) } else { privateKeyBytes, proofPrivateKeyBytes, err := secretStore.ExportAccountKeysForBackup() require.NoError(t, err, "ExportAccountKeysForBackup error") secretStore, err = secretstore.NewInMemSecretStore(nil) require.NoError(t, err) require.NoError(t, secretStore.ImportAccountKeys(privateKeyBytes, proofPrivateKeyBytes)) } db, err := NewWeshOrbitDB(ctx, ca.API(), &NewOrbitDBOptions{ NewOrbitDBOptions: orbitdb.NewOrbitDBOptions{ Logger: logger, }, SecretStore: secretStore, }) if err != nil { t.Fatal(err) } gc, err := db.OpenGroup(ctx, group, nil) if err != nil { t.Fatalf("err: creating new group context, %v", err) } mp := &mockedPeer{ CoreAPI: ca, DB: db, GC: gc, SecretStore: secretStore, } // setup cleanup cls[i] = func() { if ms := mp.GC.MetadataStore(); ms != nil { err := ms.Drop() assert.NoError(t, err) } gc.Close() if db := mp.DB; db != nil { assert.NoError(t, err) err = db.Close() assert.NoError(t, err) } _ = secretStore.Close() } mockedPeers[deviceIndex] = mp deviceIndex++ } } connectPeers(ctx, t, ipfsopts.Mocknet) return mockedPeers, groupPrivateKey, func() { for _, cleanup := range cls { cleanup() } cleanupLogger() } } func connectPeers(ctx context.Context, t testing.TB, mn mocknet.Mocknet) { t.Helper() err := mn.LinkAll() require.NoError(t, err) err = mn.ConnectAllButSelf() require.NoError(t, err) } func dropPeers(t *testing.T, mockedPeers []*mockedPeer) { t.Helper() for _, m := range mockedPeers { if ms := m.GC.MetadataStore(); ms != nil { if err := ms.Drop(); err != nil { t.Fatal(err) } } if db := m.DB; db != nil { if err := db.Close(); err != nil { t.Fatal(err) } } if ca := m.CoreAPI; ca != nil { if err := ca.MockNode().Close(); err != nil { t.Fatal(err) } } } } type ServiceMethods interface { GetContextGroupForID(id []byte) (*GroupContext, error) } func GetRootDatastoreForPath(dir string, key []byte, salt []byte, logger *zap.Logger) (datastore.Batching, error) { inMemory := dir == InMemoryDir var ds datastore.Batching if inMemory { ds = datastore.NewMapDatastore() } else { err := os.MkdirAll(dir, os.ModePerm) if err != nil { return nil, errcode.ErrCode_TODO.Wrap(err) } dbPath := filepath.Join(dir, "datastore.sqlite") sqldsOpts := encrepo.SQLCipherDatastoreOptions{JournalMode: "WAL", PlaintextHeader: len(salt) != 0, Salt: salt} ds, err = encrepo.NewSQLCipherDatastore("sqlite3", dbPath, "blocks", key, sqldsOpts) if err != nil { return nil, errcode.ErrCode_TODO.Wrap(err) } } ds = ds_sync.MutexWrap(ds) return ds, nil } func CreateMultiMemberGroupInstance(ctx context.Context, t *testing.T, tps ...*TestingProtocol) *protocoltypes.Group { testutil.LogTree(t, "Create and Join MultiMember Group", 0, true) start := time.Now() ntps := len(tps) // Create group group, _, err := NewGroupMultiMember() require.NoError(t, err) // Get Instance Configurations { testutil.LogTree(t, "Get Instance Configuration", 1, true) start := time.Now() // check if everything is ready for _, pt := range tps { _, err := pt.Client.ServiceGetConfiguration(ctx, &protocoltypes.ServiceGetConfiguration_Request{}) require.NoError(t, err) } testutil.LogTree(t, "duration: %s", 1, false, time.Since(start)) } // Join Group { testutil.LogTree(t, "Join Group", 1, true) start := time.Now() for _, pt := range tps { req := protocoltypes.MultiMemberGroupJoin_Request{ Group: group, } // pt join group _, err = pt.Client.MultiMemberGroupJoin(ctx, &req) require.NoError(t, err) } testutil.LogTree(t, "duration: %s", 1, false, time.Since(start)) } // Get Member/Device PKs memberPKs := make([][]byte, ntps) devicePKs := make([][]byte, ntps) { testutil.LogTree(t, "Get Member/Device PKs", 1, true) start := time.Now() for i, pt := range tps { res, err := pt.Client.GroupInfo(ctx, &protocoltypes.GroupInfo_Request{ GroupPk: group.PublicKey, }) require.NoError(t, err) assert.Equal(t, group.PublicKey, res.Group.PublicKey) memberPKs[i] = res.MemberPk devicePKs[i] = res.DevicePk } testutil.LogTree(t, "duration: %s", 1, false, time.Since(start)) } // Activate Group { testutil.LogTree(t, "Activate Group", 1, true) start := time.Now() for i, pt := range tps { _, err := pt.Client.ActivateGroup(ctx, &protocoltypes.ActivateGroup_Request{ GroupPk: group.PublicKey, }) assert.NoError(t, err, fmt.Sprintf("error for client %d", i)) } testutil.LogTree(t, "duration: %s", 1, false, time.Since(start)) } // Exchange Secrets { testutil.LogTree(t, "Exchange Secrets", 1, true) start := time.Now() wg := sync.WaitGroup{} secretsReceivedLock := sync.Mutex{} secretsReceived := make([]map[string]struct{}, ntps) wg.Add(ntps) nSuccess := int64(0) for i := range tps { go func(i int) { tp := tps[i] defer wg.Done() secretsReceived[i] = map[string]struct{}{} ctx, cancel := context.WithCancel(ctx) defer cancel() sub, inErr := tp.Client.GroupMetadataList(ctx, &protocoltypes.GroupMetadataList_Request{ GroupPk: group.PublicKey, }) if inErr != nil { assert.NoError(t, err, fmt.Sprintf("error for client %d", i)) return } for { evt, inErr := sub.Recv() if inErr != nil { if inErr != io.EOF { assert.NoError(t, err, fmt.Sprintf("error for client %d", i)) } break } if source, err := isEventAddSecretTargetedToMember(memberPKs[i], evt); err != nil { tps[i].Opts.Logger.Error("err:", zap.Error(inErr)) assert.NoError(t, err, fmt.Sprintf("error for client %d", i)) break } else if source != nil { secretsReceivedLock.Lock() secretsReceived[i][string(source)] = struct{}{} done := len(secretsReceived[i]) == ntps secretsReceivedLock.Unlock() if done { n := atomic.AddInt64(&nSuccess, 1) got := fmt.Sprintf("%d/%d", n, ntps) tps[i].Opts.Logger.Debug("received all secrets", zap.String("ok", got)) return } } } }(i) } wg.Wait() secretsReceivedLock.Lock() ok := true for i := range secretsReceived { if !assert.Equal(t, ntps, len(secretsReceived[i]), fmt.Sprintf("mismatch for client %d", i)) { ok = false } } require.True(t, ok) secretsReceivedLock.Unlock() testutil.LogTree(t, "duration: %s", 1, false, time.Since(start)) } testutil.LogTree(t, "duration: %s", 0, false, time.Since(start)) return group } func isEventAddSecretTargetedToMember(ownRawPK []byte, evt *protocoltypes.GroupMetadataEvent) ([]byte, error) { // Only count EventTypeGroupDeviceChainKeyAdded events if evt.Metadata.EventType != protocoltypes.EventType_EventTypeGroupDeviceChainKeyAdded { return nil, nil } sec := &protocoltypes.GroupDeviceChainKeyAdded{} err := proto.Unmarshal(evt.Event, sec) if err != nil { return nil, err } // Filter out events targeted at other members if !bytes.Equal(ownRawPK, sec.DestMemberPk) { return nil, nil } return sec.DevicePk, nil } ================================================ FILE: testing_test.go ================================================ package weshnet import ( "context" "fmt" "os" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "berty.tech/weshnet/v2/pkg/protocoltypes" ) func TestClient_impl(t *testing.T) { var _ Service = (*service)(nil) var _ protocoltypes.ProtocolServiceServer = (*service)(nil) } func TestEmptyArgs(t *testing.T) { // disable resources manager for test os.Setenv("LIBP2P_RCMGR", "false") // initialize new client client, err := NewService(Opts{}) require.NoError(t, err) err = client.Close() require.NoError(t, err) client.Close() } func TestTestingProtocol(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) opts := TestingOpts{} tp, cleanup := NewTestingProtocol(ctx, t, &opts, nil) assert.NotNil(t, tp) cleanup() cancel() } func TestTestingProtocolWithMockedPeers(t *testing.T) { for amount := 0; amount < 5; amount++ { t.Run(fmt.Sprintf("%d-peers", amount), func(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) opts := TestingOpts{} tp, cleanup := NewTestingProtocolWithMockedPeers(ctx, t, &opts, nil, amount) assert.NotNil(t, tp) cleanup() cancel() }) } } ================================================ FILE: tinder_swiper.go ================================================ package weshnet import ( "context" "encoding/base64" "encoding/hex" "fmt" mrand "math/rand" "sync" "time" pubsub "github.com/libp2p/go-libp2p-pubsub" "github.com/libp2p/go-libp2p/core/peer" backoff "github.com/libp2p/go-libp2p/p2p/discovery/backoff" "go.uber.org/zap" "moul.io/srand" "berty.tech/weshnet/v2/pkg/logutil" "berty.tech/weshnet/v2/pkg/rendezvous" tinder "berty.tech/weshnet/v2/pkg/tinder" "berty.tech/weshnet/v2/pkg/tyber" ) type swiperRequest struct { bstrat backoff.BackoffStrategy wgRefresh *sync.WaitGroup ctx context.Context out chan<- peer.AddrInfo rdvTopic string } type Swiper struct { topics map[string]*pubsub.Topic backoffFactory backoff.BackoffFactory inprogressLookup map[string]*swiperRequest muRequest sync.Mutex rp *rendezvous.RotationInterval logger *zap.Logger tinder *tinder.Service } func NewSwiper(logger *zap.Logger, tinder *tinder.Service, rp *rendezvous.RotationInterval) *Swiper { // we need to use math/rand here, but it is seeded from crypto/rand srand := mrand.New(mrand.NewSource(srand.MustSecure())) // nolint:gosec backoffstrat := backoff.NewExponentialBackoff(time.Second, time.Minute*10, backoff.FullJitter, time.Second, 30.0, 0, srand) return &Swiper{ backoffFactory: backoffstrat, logger: logger.Named("swiper"), topics: make(map[string]*pubsub.Topic), inprogressLookup: make(map[string]*swiperRequest), rp: rp, tinder: tinder, } } func (s *Swiper) RefreshContactRequest(ctx context.Context, topic []byte) (addrs []peer.AddrInfo, err error) { ctx, _, endSection := tyber.Section(ctx, s.logger, "swiper starting refresh: "+hex.EncodeToString(topic)) defer func() { endSection(err, "") }() // canceling find peers s.muRequest.Lock() req, ok := s.inprogressLookup[base64.StdEncoding.EncodeToString(topic)] if !ok { err = fmt.Errorf("unknown topic") s.muRequest.Unlock() return addrs, err } // add a refresh job process req.wgRefresh.Add(1) defer req.wgRefresh.Done() s.muRequest.Unlock() ctx, cancel := context.WithCancel(ctx) defer cancel() go func() { // if rotation topic is outdated, cancel research <-req.ctx.Done() cancel() }() // force find peers re check topic cpeer := s.tinder.FindPeers(req.ctx, req.rdvTopic) select { case p := <-cpeer: req.out <- p return []peer.AddrInfo{p}, nil case <-ctx.Done(): return nil, ctx.Err() } } // WatchTopic looks for peers providing a resource. // 'done' is used to alert parent when everything is done, to avoid data races. func (s *Swiper) WatchTopic(ctx context.Context, topic, seed []byte) <-chan peer.AddrInfo { ctx, _, endSection := tyber.Section(ctx, s.logger, "swiper looking for peers: "+hex.EncodeToString(topic)) s.muRequest.Lock() defer s.muRequest.Unlock() s.logger.Debug("start watch for peer with", logutil.PrivateString("topic", base64.StdEncoding.EncodeToString(topic)), zap.String("seed", string(seed))) var point *rendezvous.Point cpeers := make(chan peer.AddrInfo) go func() { defer endSection(nil, "watch topic ended") defer close(cpeers) wgRefresh := sync.WaitGroup{} for ctx.Err() == nil { if point == nil || time.Now().After(point.Deadline()) { point = s.rp.NewRendezvousPointForPeriod(time.Now(), base64.StdEncoding.EncodeToString(topic), seed) } bstrat := s.backoffFactory() // store watch peers information to be later used by the refresh method to force a lookup s.muRequest.Lock() wctx, cancel := context.WithCancel(ctx) s.inprogressLookup[base64.StdEncoding.EncodeToString(topic)] = &swiperRequest{ bstrat: bstrat, wgRefresh: &wgRefresh, ctx: wctx, out: cpeers, rdvTopic: point.RotationTopic(), } s.muRequest.Unlock() // start looking for peers for the given rotation topic s.logger.Debug("looking for peers", logutil.PrivateString("topic", point.RotationTopic())) if err := s.watchPeers(wctx, bstrat, cpeers, point.RotationTopic()); err != nil && err != context.DeadlineExceeded { s.logger.Debug("watch until deadline ended", zap.Error(err)) } cancel() // at this point upper context is done or we need to refresh // rotation point. // take a little breath and wait one second to avoid calling find // peer in short amount of time time.Sleep(time.Second) } s.muRequest.Lock() delete(s.inprogressLookup, base64.StdEncoding.EncodeToString(topic)) s.muRequest.Unlock() // wait all refresh job are done before closing the channel // we dont want to send peer on a closed channel wgRefresh.Wait() }() return cpeers } func (s *Swiper) watchPeers(ctx context.Context, _ backoff.BackoffStrategy, out chan<- peer.AddrInfo, topic string) error { sub := s.tinder.Subscribe(topic) defer sub.Close() // func () { // if err := sub.Close(); err != nil { // s.logger.Error("unable to close sub properly", zap.Error(err)) // } // }() s.logger.Debug("swipper watch peers started", logutil.PrivateString("topic", topic)) // start findpeers for pulls go func() { timeout := time.Minute // @TODO(gfanton): do we need to use backoffstartegy here ? for ctx.Err() == nil { s.logger.Debug("swiper pulling for peers", logutil.PrivateString("topic", topic)) if err := sub.Pull(); err != nil { s.logger.Error("unable to pull for peer on subscription", zap.Error(err)) } select { case <-time.After(timeout): case <-ctx.Done(): } } }() for { // wait until the context is done select { case p := <-sub.Out(): s.logger.Debug("found a peers", logutil.PrivateString("topic", topic), zap.String("peer", p.String())) out <- p case <-ctx.Done(): s.logger.Debug("watch peers done", logutil.PrivateString("topic", topic)) return ctx.Err() } } } // watch looks for peers providing a resource func (s *Swiper) Announce(ctx context.Context, topic, seed []byte) { var point *rendezvous.Point s.logger.Debug("start announce for peer with", logutil.PrivateString("topic", base64.StdEncoding.EncodeToString(topic)), logutil.PrivateString("seed", string(seed))) go func() { for ctx.Err() == nil { if point == nil || time.Now().After(point.Deadline()) { point = s.rp.NewRendezvousPointForPeriod(time.Now(), base64.StdEncoding.EncodeToString(topic), seed) } s.logger.Debug("self announce topic for time", logutil.PrivateString("topic", point.RotationTopic())) actx, cancel := context.WithDeadline(ctx, point.Deadline()) if err := s.tinder.StartAdvertises(actx, point.RotationTopic()); err != nil && err != ctx.Err() { cancel() <-time.After(time.Second * 10) // retry after 10sc continue } select { case <-actx.Done(): s.logger.Debug("rotation ended", logutil.PrivateString("topic", point.RotationTopic())) case <-ctx.Done(): s.logger.Debug("announce advertise ended", logutil.PrivateString("topic", point.RotationTopic()), zap.Error(ctx.Err())) } cancel() } }() } ================================================ FILE: tinder_swiper_test.go ================================================ package weshnet import ( "context" "fmt" "testing" "time" mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "berty.tech/weshnet/v2/pkg/ipfsutil" "berty.tech/weshnet/v2/pkg/rendezvous" "berty.tech/weshnet/v2/pkg/testutil" "berty.tech/weshnet/v2/pkg/tinder" ) func TestAnnounceWatchForPeriod(t *testing.T) { testutil.FilterSpeed(t, testutil.Slow) cases := []struct { expectedPeersFound int topicA []byte topicB []byte seedA []byte seedB []byte }{ { expectedPeersFound: 0, topicA: []byte("topicA"), topicB: []byte("topicB"), seedA: []byte("seedA"), seedB: []byte("seedA"), }, { expectedPeersFound: 1, topicA: []byte("topicA"), topicB: []byte("topicA"), seedA: []byte("seedA"), seedB: []byte("seedA"), }, } logger, cleanup := testutil.Logger(t) defer cleanup() for i, tc := range cases { t.Run(fmt.Sprintf("tc: %d", i), func(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() mn := mocknet.New() defer mn.Close() opts := &ipfsutil.TestingAPIOpts{ Logger: logger, Mocknet: mn, DiscoveryServer: tinder.NewMockDriverServer(), } apiA := ipfsutil.TestingCoreAPIUsingMockNet(ctx, t, opts) apiB := ipfsutil.TestingCoreAPIUsingMockNet(ctx, t, opts) err := mn.LinkAll() require.NoError(t, err) err = mn.ConnectAllButSelf() require.NoError(t, err) rpA := rendezvous.NewRotationInterval(time.Hour) rpB := rendezvous.NewRotationInterval(time.Hour) swiperA := NewSwiper(opts.Logger, apiA.Tinder(), rpA) swiperB := NewSwiper(opts.Logger, apiB.Tinder(), rpB) swiperA.Announce(ctx, tc.topicA, tc.seedA) time.Sleep(time.Millisecond * 100) cpeers := swiperB.WatchTopic(ctx, tc.topicB, tc.seedB) var foundPeers int loop: for foundPeers = 0; foundPeers < tc.expectedPeersFound; foundPeers++ { select { case <-ctx.Done(): break loop case <-cpeers: } } assert.Equal(t, len(cpeers), 0) assert.Equal(t, tc.expectedPeersFound, foundPeers) }) } } func TestAnnounceForPeriod(t *testing.T) { } ================================================ FILE: tool/bench-cellular/.gitignore ================================================ bench ================================================ FILE: tool/bench-cellular/Makefile ================================================ SRCS := $(wildcard *.go) bench: $(SRCS) go build $(SRCS) @echo "Now run './bench -h'" help: bench ./bench -h ================================================ FILE: tool/bench-cellular/bench.go ================================================ package main import ( "context" crand "crypto/rand" "flag" "fmt" "io" mrand "math/rand" "os" "strings" golog "github.com/ipfs/go-log" "github.com/libp2p/go-libp2p" quict "github.com/libp2p/go-libp2p-quic-transport" "github.com/libp2p/go-libp2p/core/crypto" tcpt "github.com/libp2p/go-libp2p/p2p/transport/tcp" ma "github.com/multiformats/go-multiaddr" "github.com/peterbourgon/ff/v3/ffcli" ) const ( benchDownloadPID = "/bench/download/1.0.0" benchUploadPID = "/bench/upload/1.0.0" ) type globalOpts struct { tcp bool insecure bool seed int64 verbose bool veryVerbose bool } func globalOptsToLibp2pOpts(gOpts *globalOpts) ([]libp2p.Option, error) { var ( r io.Reader opts []libp2p.Option ) if gOpts.seed == 0 { r = crand.Reader } else { r = mrand.New(mrand.NewSource(gOpts.seed)) } priv, _, err := crypto.GenerateKeyPairWithReader(crypto.RSA, crypto.MinRsaKeyBits, r) if err != nil { return nil, err } opts = append(opts, libp2p.Identity(priv)) if gOpts.tcp { opts = append(opts, libp2p.Transport(tcpt.NewTCPTransport)) } else { opts = append(opts, libp2p.Transport(quict.NewTransport)) } if gOpts.insecure { opts = append(opts, libp2p.NoSecurity) } return opts, nil } func main() { var ( ctx = context.Background() gOpts = &globalOpts{} sOpts = &serverOpts{} cOpts = &clientOpts{} ) var serverCommand *ffcli.Command { serverFs := flag.NewFlagSet("server", flag.ExitOnError) serverFs.IntVar(&sOpts.port, "port", 0, "port to listen on (default: random)") serverFs.BoolVar(&sOpts.ip6, "ip6", false, "use ipv6 instead of ipv4") serverFs.StringVar(&sOpts.relay, "relay", staticBertyRelayMode, fmt.Sprintf("set relay mode, possible values: '%s', '%s', '%s' or '%s'", staticBertyRelayMode, staticIPFSRelayMode, discoveryRelayMode, disabledRelayMode)) serverCommand = &ffcli.Command{ Name: "server", ShortUsage: "bench server [flags]", ShortHelp: "run a benchmark server that listen for client request", FlagSet: serverFs, Exec: func(ctx context.Context, args []string) error { if len(args) > 0 { return flag.ErrHelp } if sOpts.relay != staticBertyRelayMode && sOpts.relay != staticIPFSRelayMode && sOpts.relay != discoveryRelayMode && sOpts.relay != disabledRelayMode { fmt.Fprintf(os.Stderr, "error: invalid value for -relay flag: %s\n\n", sOpts.relay) return flag.ErrHelp } if gOpts.verbose { golog.SetAllLoggers(golog.LevelError) golog.SetLogLevel("autorelay", "debug") golog.SetLogLevel("autonat", "debug") golog.SetLogLevel("basichost", "debug") golog.SetLogLevel("swarm2", "debug") } if gOpts.veryVerbose { golog.SetAllLoggers(golog.LevelDebug) } return runServer(ctx, gOpts, sOpts) }, } } var clientCommand *ffcli.Command { const megabyte = 1048576 clientFs := flag.NewFlagSet("client", flag.ExitOnError) clientFs.StringVar(&cOpts.dest, "dest", "", "server multiaddr to dial") clientFs.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)) clientFs.BoolVar(&cOpts.reco, "reco", false, "test reconnection to server") clientFs.IntVar(&cOpts.size, "size", megabyte, "size (in bytes) of data to upload / download (default: 1MB)") clientCommand = &ffcli.Command{ Name: "client", ShortUsage: "bench client [flags]", ShortHelp: "run a benchmark client that send request to server", FlagSet: clientFs, Exec: func(ctx context.Context, args []string) error { if len(args) > 0 { return flag.ErrHelp } requestTypes := strings.Split(cOpts.request, ",") if len(requestTypes) == 0 { fmt.Fprintf(os.Stderr, "error: at least one request type must be specified using -request flag\n\n") return flag.ErrHelp } for _, requestType := range requestTypes { trimed := strings.TrimSpace(requestType) if trimed != pingRequestType && trimed != uploadRequestType && trimed != downloadRequestType { fmt.Fprintf(os.Stderr, "error: invalid request type specified using -request flag: '%s'\n\n", trimed) return flag.ErrHelp } } if cOpts.dest == "" { fmt.Fprintf(os.Stderr, "error: a server multiaddr must be specified using -dest flag\n\n") return flag.ErrHelp } if _, err := ma.NewMultiaddr(cOpts.dest); err != nil { fmt.Fprintf(os.Stderr, "error: invalid multiaddr specified using -dest flag: %v\n\n", err) return flag.ErrHelp } if cOpts.size <= 0 { fmt.Fprintf(os.Stderr, "error: a positive bytes amount must be set using -size flag (default 1MB)\n\n") return flag.ErrHelp } if gOpts.verbose { golog.SetAllLoggers(golog.LevelError) golog.SetLogLevel("basichost", "debug") golog.SetLogLevel("swarm2", "debug") } if gOpts.veryVerbose { golog.SetAllLoggers(golog.LevelDebug) } return runClient(ctx, gOpts, cOpts) }, } } var rootCommand *ffcli.Command { rootFs := flag.NewFlagSet("root", flag.ExitOnError) rootFs.BoolVar(&gOpts.tcp, "tcp", false, "use TCP instead of QUIC") rootFs.BoolVar(&gOpts.insecure, "insecure", false, "use an unencrypted connection") rootFs.Int64Var(&gOpts.seed, "seed", 0, "set random seed for id generation") rootFs.BoolVar(&gOpts.verbose, "v", false, "verbose mode: print debug level for relevant libp2p loggers") rootFs.BoolVar(&gOpts.veryVerbose, "vv", false, "very verbose mode: print debug level for all libp2p loggers") rootCommand = &ffcli.Command{ ShortUsage: "bench [flags] [subcommand_flags]", FlagSet: rootFs, Exec: func(context.Context, []string) error { return flag.ErrHelp }, Subcommands: []*ffcli.Command{ serverCommand, clientCommand, }, } } err := rootCommand.ParseAndRun(ctx, os.Args[1:]) if err == flag.ErrHelp { os.Exit(1) } else if err != nil { fmt.Fprintf(os.Stderr, "error: %v\n", err) os.Exit(2) } } ================================================ FILE: tool/bench-cellular/client.go ================================================ package main import ( "bufio" "context" "fmt" "io" "log" "math/rand" "os" "strings" "time" "github.com/libp2p/go-libp2p" peer "github.com/libp2p/go-libp2p-peer" "github.com/libp2p/go-libp2p/core/host" peerstore "github.com/libp2p/go-libp2p/p2p/host/peerstore" p2pping "github.com/libp2p/go-libp2p/p2p/protocol/ping" ma "github.com/multiformats/go-multiaddr" ) const ( pingRequestType = "ping" uploadRequestType = "upload" downloadRequestType = "download" ) type clientOpts struct { dest string request string reco bool size int } func createClientHost(ctx context.Context, gOpts *globalOpts) (host.Host, error) { opts, err := globalOptsToLibp2pOpts(gOpts) // Get identity and transport if err != nil { return nil, err } opts = append( opts, libp2p.ListenAddrs(), // On client mode, set no listener ) return libp2p.New(ctx, opts...) // Create host } func addDestToPeerstore(h host.Host, dest string) (peer.ID, error) { maddr, err := ma.NewMultiaddr(dest) if err != nil { return "", err } var pid string if _, err := maddr.ValueForProtocol(ma.P_CIRCUIT); err == nil { first := true // Get the second peerid (target), the first being the relay peerid ma.ForEach(maddr, func(c ma.Component) bool { if c.Protocol().Code == ma.P_IPFS { if first { first = false } else { pid = c.Value() return false } } return true }) } else { pid, err = maddr.ValueForProtocol(ma.P_IPFS) if err != nil { return "", err } } peerid, err := peer.IDB58Decode(pid) if err != nil { return "", err } if _, err := maddr.ValueForProtocol(ma.P_CIRCUIT); err != nil { targetAddr, _ := ma.NewMultiaddr(fmt.Sprintf("/ipfs/%s", pid)) maddr = maddr.Decapsulate(targetAddr) } h.Peerstore().AddAddr(peerid, maddr, peerstore.PermanentAddrTTL) return peerid, nil } func ping(ctx context.Context, h host.Host, peerid peer.ID) error { var ( timeout = 30 * time.Second timeoutCtx, cancel = context.WithTimeout(ctx, timeout) resultOccured = 0 resultRequired = 8 start = time.Now() ) defer cancel() log.Printf("New ping started with timeout: %v", timeout) for result := range p2pping.Ping(timeoutCtx, h, peerid) { if result.Error != nil { return fmt.Errorf("ping error: %v", result.Error) } log.Printf("\tPing RTT: %v", result.RTT) resultOccured++ if resultOccured >= resultRequired { break } } if resultOccured < resultRequired { return fmt.Errorf("ping request timeouted after %v: %d/%d (RTT done/required)", timeout, resultOccured, resultRequired) } log.Printf("Ping request with %d RTT took: %v", resultOccured, time.Since(start)) return nil } func upload(ctx context.Context, h host.Host, peerid peer.ID, cOpts *clientOpts) error { start := time.Now() su, err := h.NewStream(ctx, peerid, benchUploadPID) if err != nil { return fmt.Errorf("new upload stream failed: %v", err) } reader := bufio.NewReader(su) if _, err = reader.ReadString('\n'); err != nil { return fmt.Errorf("read error during stream opened ack: %v", err) } log.Printf("New upload stream took: %v", time.Since(start)) data := make([]byte, cOpts.size) rand.Read(data) start = time.Now() if _, err = su.Write(data); err != nil { return fmt.Errorf("write error during upload: %v", err) } su.CloseWrite() if _, err = reader.ReadString('\n'); err != nil { return fmt.Errorf("read error during uploaded ack: %v", err) } log.Printf("Data (%d bytes) upload took: %v", cOpts.size, time.Since(start)) su.CloseRead() return nil } func download(ctx context.Context, h host.Host, peerid peer.ID, cOpts *clientOpts) error { start := time.Now() sd, err := h.NewStream(ctx, peerid, benchDownloadPID) if err != nil { return fmt.Errorf("new download stream failed: %v", err) } reader := bufio.NewReader(sd) if _, err = reader.ReadString('\n'); err != nil { return fmt.Errorf("read error during stream opened ack: %v", err) } log.Printf("New download stream took: %v", time.Since(start)) // Send size to download sizeStr := fmt.Sprintf("%d\n", cOpts.size) if _, err = sd.Write([]byte(sizeStr)); err != nil { return fmt.Errorf("write size error during download: %v", err) } start = time.Now() if _, err = io.ReadAll(sd); err != nil { return err } log.Printf("Data (%d bytes) download took: %v", cOpts.size, time.Since(start)) sd.Close() return nil } func runClient(ctx context.Context, gOpts *globalOpts, cOpts *clientOpts) error { h, err := createClientHost(ctx, gOpts) if err != nil { return fmt.Errorf("client host creation failed: %v", err) } log.Println("Local peerID:", h.ID().String()) peerid, err := addDestToPeerstore(h, cOpts.dest) if err != nil { return err } requestList := strings.Split(cOpts.request, ",") for i, request := range requestList { requestList[i] = strings.TrimSpace(request) } for { log.Printf("Playing request list: %q", requestList) start := time.Now() for _, request := range requestList { switch request { case pingRequestType: err = ping(ctx, h, peerid) case uploadRequestType: err = upload(ctx, h, peerid, cOpts) case downloadRequestType: err = download(ctx, h, peerid, cOpts) } if err != nil { return err } } log.Printf("Playing request list took: %v", time.Since(start)) if cOpts.reco { cOpts.reco = false reader := bufio.NewReader(os.Stdin) fmt.Printf("%s Reconnection test: switch connection then press enter... ", time.Now().Format("2006/01/02 15:04:05")) _, _ = reader.ReadString('\n') log.Print("Reconnection test: replay request list using new connection") continue } break } return nil } ================================================ FILE: tool/bench-cellular/go.mod ================================================ module berty.tech/weshnet/tool/bench-cellular go 1.15 require ( github.com/ipfs/go-log v1.0.4 github.com/libp2p/go-libp2p v0.13.0 github.com/libp2p/go-libp2p/core v0.8.0 github.com/libp2p/go-libp2p-kad-dht v0.11.1 github.com/libp2p/go-libp2p-peer v0.2.0 github.com/libp2p/go-libp2p-peerstore v0.2.6 github.com/libp2p/go-libp2p-quic-transport v0.10.0 github.com/libp2p/go-libp2p-routing v0.1.0 github.com/libp2p/go-tcp-transport v0.2.1 github.com/multiformats/go-multiaddr v0.3.1 github.com/peterbourgon/ff/v3 v3.0.0 ) ================================================ FILE: tool/bench-cellular/go.sum ================================================ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Kubuxu/go-os-helper v0.0.1/go.mod h1:N8B+I7vPCT80IcP58r50u4+gEEcsZETFUpAzWW2ep1Y= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/btcsuite/btcd v0.0.0-20190213025234-306aecffea32/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8= github.com/btcsuite/btcd v0.0.0-20190523000118-16327141da8c/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20190207003914-4c204d697803/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE= github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davidlazar/go-crypto v0.0.0-20170701192655-dcfb0a7ac018 h1:6xT9KW8zLC5IlbaIF5Q7JNieBoACT7iW0YTxQHR0in0= github.com/davidlazar/go-crypto v0.0.0-20170701192655-dcfb0a7ac018/go.mod h1:rQYf4tfk5sSwFsnDg3qYaBxSjsD9S8+59vW0dKUgme4= github.com/dgraph-io/badger v1.5.5-0.20190226225317-8115aed38f8f/go.mod h1:VZxzAIRPHRVNRKRo6AXrX9BJegn6il06VMTZVJYCIjQ= github.com/dgraph-io/badger v1.6.0-rc1/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= github.com/dgraph-io/badger v1.6.1/go.mod h1:FRmFw3uxvcpa8zG3Rxs0th+hCLIuaQg8HlNV5bjgnuU= github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgryski/go-farm v0.0.0-20190104051053-3adb47b1fb0f/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6 h1:u/UEqS66A5ckRmS4yNpjmVH56sVtS/RfclBAYocb4as= github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6/go.mod h1:1i71OnUq3iUe1ma7Lr6yG6/rjvM3emb6yoL7xLFzcVQ= github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9 h1:uHTyIjqVhYRhLbJ8nIiOJHkEZZ+5YoOsAbD3sk82NiE= github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gopacket v1.1.17 h1:rMrlX2ZY2UbvT+sdz3+6J+pp2z+msCq9MxTU6ymxbBY= github.com/google/gopacket v1.1.17/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM= github.com/google/gopacket v1.1.18 h1:lum7VRA9kdlvBi7/v2p7/zcbkduHaCH/SVVyurs7OpY= github.com/google/gopacket v1.1.18/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU= github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI= github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huin/goupnp v1.0.0 h1:wg75sLpL6DZqwHQN6E1Cfk6mtfzS45z8OV+ic+DtHRo= github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/ipfs/go-cid v0.0.1/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= github.com/ipfs/go-cid v0.0.2/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= github.com/ipfs/go-cid v0.0.3/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= github.com/ipfs/go-cid v0.0.4/go.mod h1:4LLaPOQwmk5z9LBgQnpkivrx8BJjUyGwTXCd5Xfj6+M= github.com/ipfs/go-cid v0.0.5/go.mod h1:plgt+Y5MnOey4vO4UlUazGqdbEXuFYitED67FexhXog= github.com/ipfs/go-cid v0.0.6/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I= github.com/ipfs/go-cid v0.0.7 h1:ysQJVJA3fNDF1qigJbsSQOdjhVLsOEoPdh0+R97k3jY= github.com/ipfs/go-cid v0.0.7/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I= github.com/ipfs/go-datastore v0.0.1/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE= github.com/ipfs/go-datastore v0.1.0/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE= github.com/ipfs/go-datastore v0.1.1/go.mod h1:w38XXW9kVFNp57Zj5knbKWM2T+KOZCGDRVNdgPHtbHw= github.com/ipfs/go-datastore v0.4.0/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA= github.com/ipfs/go-datastore v0.4.1/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA= github.com/ipfs/go-datastore v0.4.4/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA= github.com/ipfs/go-datastore v0.4.5 h1:cwOUcGMLdLPWgu3SlrCckCMznaGADbPqE0r8h768/Dg= github.com/ipfs/go-datastore v0.4.5/go.mod h1:eXTcaaiN6uOlVCLS9GjJUJtlvJfM3xk23w3fyfrmmJs= github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps= github.com/ipfs/go-ds-badger v0.0.2/go.mod h1:Y3QpeSFWQf6MopLTiZD+VT6IC1yZqaGmjvRcKeSGij8= github.com/ipfs/go-ds-badger v0.0.5/go.mod h1:g5AuuCGmr7efyzQhLL8MzwqcauPojGPUaHzfGTzuE3s= github.com/ipfs/go-ds-badger v0.0.7/go.mod h1:qt0/fWzZDoPW6jpQeqUjR5kBfhDNB65jd9YlmAvpQBk= github.com/ipfs/go-ds-badger v0.2.1/go.mod h1:Tx7l3aTph3FMFrRS838dcSJh+jjA7cX9DrGVwx/NOwE= github.com/ipfs/go-ds-badger v0.2.3/go.mod h1:pEYw0rgg3FIrywKKnL+Snr+w/LjJZVMTBRn4FS6UHUk= github.com/ipfs/go-ds-leveldb v0.0.1/go.mod h1:feO8V3kubwsEF22n0YRQCffeb79OOYIykR4L04tMOYc= github.com/ipfs/go-ds-leveldb v0.1.0/go.mod h1:hqAW8y4bwX5LWcCtku2rFNX3vjDZCy5LZCg+cSZvYb8= github.com/ipfs/go-ds-leveldb v0.4.1/go.mod h1:jpbku/YqBSsBc1qgME8BkWS4AxzF2cEu1Ii2r79Hh9s= github.com/ipfs/go-ds-leveldb v0.4.2/go.mod h1:jpbku/YqBSsBc1qgME8BkWS4AxzF2cEu1Ii2r79Hh9s= github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= github.com/ipfs/go-ipfs-util v0.0.1/go.mod h1:spsl5z8KUnrve+73pOhSVZND1SIxPW5RyBCNzQxlJBc= github.com/ipfs/go-ipfs-util v0.0.2 h1:59Sswnk1MFaiq+VcaknX7aYEyGyGDAA73ilhEK2POp8= github.com/ipfs/go-ipfs-util v0.0.2/go.mod h1:CbPtkWJzjLdEcezDns2XYaehFVNXG9zrdrtMecczcsQ= github.com/ipfs/go-ipns v0.0.2 h1:oq4ErrV4hNQ2Eim257RTYRgfOSV/s8BDaf9iIl4NwFs= github.com/ipfs/go-ipns v0.0.2/go.mod h1:WChil4e0/m9cIINWLxZe1Jtf77oz5L05rO2ei/uKJ5U= github.com/ipfs/go-log v0.0.1/go.mod h1:kL1d2/hzSpI0thNYjiKfjanbVNU+IIGA/WnNESY9leM= github.com/ipfs/go-log v1.0.2/go.mod h1:1MNjMxe0u6xvJZgeqbJ8vdo2TKaGwZ1a0Bpza+sr2Sk= github.com/ipfs/go-log v1.0.3/go.mod h1:OsLySYkwIbiSUR/yBTdv1qPtcE4FW3WPWk/ewz9Ru+A= github.com/ipfs/go-log v1.0.4 h1:6nLQdX4W8P9yZZFH7mO+X/PzjN8Laozm/lMJ6esdgzY= github.com/ipfs/go-log v1.0.4/go.mod h1:oDCg2FkjogeFOhqqb+N39l2RpTNPL6F/StPkB3kPgcs= github.com/ipfs/go-log/v2 v2.0.2/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0= github.com/ipfs/go-log/v2 v2.0.3/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0= github.com/ipfs/go-log/v2 v2.0.5/go.mod h1:eZs4Xt4ZUJQFM3DlanGhy7TkwwawCZcSByscwkWG+dw= github.com/ipfs/go-log/v2 v2.1.1 h1:G4TtqN+V9y9HY9TA6BwbCVyyBZ2B9MbCjR2MtGx8FR0= github.com/ipfs/go-log/v2 v2.1.1/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHntrv9KM= github.com/jackpal/gateway v1.0.5/go.mod h1:lTpwd4ACLXmpyiCTRtfiNyVnUmqT9RivzCDQetPfnjA= github.com/jackpal/go-nat-pmp v1.0.1/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jbenet/go-cienv v0.0.0-20150120210510-1bb1476777ec/go.mod h1:rGaEvXB4uRSZMmzKNLoXvTu1sfx+1kv/DojUlPrSZGs= github.com/jbenet/go-cienv v0.1.0 h1:Vc/s0QbQtoxX8MwwSLWWh+xNNZvM3Lw7NsTcHrvvhMc= github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA= github.com/jbenet/go-temp-err-catcher v0.0.0-20150120210811-aac704a3f4f2/go.mod h1:8GXXJV31xl8whumTzdZsTt3RnUIiPqzkyf7mxToRCMs= github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABoLk/+KKHggpk= github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk= github.com/jbenet/goprocess v0.0.0-20160826012719-b497e2f366b8/go.mod h1:Ly/wlsjFq/qrU3Rar62tu1gASgGw6chQbSh/XgIIXCY= github.com/jbenet/goprocess v0.1.3/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= github.com/jbenet/goprocess v0.1.4 h1:DRGOFReOMqqDNXwW70QkacFW0YN9QnwLV0Vqk+3oU0o= github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d/go.mod h1:P2viExyCEfeWGU259JnaQ34Inuec4R38JCyBx2edgD0= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d h1:68u9r4wEvL3gYg2jvAOgROwZ3H+Y3hIDk4tbbmIjcYQ= github.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/libp2p/go-addr-util v0.0.1/go.mod h1:4ac6O7n9rIAKB1dnd+s8IbbMXkt+oBpzX4/+RACcnlQ= github.com/libp2p/go-addr-util v0.0.2 h1:7cWK5cdA5x72jX0g8iLrQWm5TRJZ6CzGdPEhWj7plWU= github.com/libp2p/go-addr-util v0.0.2/go.mod h1:Ecd6Fb3yIuLzq4bD7VcywcVSBtefcAwnUISBM3WG15E= github.com/libp2p/go-buffer-pool v0.0.1/go.mod h1:xtyIz9PMobb13WaxR6Zo1Pd1zXJKYg0a8KiIvDp3TzQ= github.com/libp2p/go-buffer-pool v0.0.2 h1:QNK2iAFa8gjAe1SPz6mHSMuCcjs+X1wlHzeOSqcmlfs= github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM= github.com/libp2p/go-cidranger v1.1.0 h1:ewPN8EZ0dd1LSnrtuwd4709PXVcITVeuwbag38yPW7c= github.com/libp2p/go-cidranger v1.1.0/go.mod h1:KWZTfSr+r9qEo9OkI9/SIEeAtw+NNoU0dXIXt15Okic= github.com/libp2p/go-conn-security-multistream v0.1.0/go.mod h1:aw6eD7LOsHEX7+2hJkDxw1MteijaVcI+/eP2/x3J1xc= github.com/libp2p/go-conn-security-multistream v0.2.0 h1:uNiDjS58vrvJTg9jO6bySd1rMKejieG7v45ekqHbZ1M= github.com/libp2p/go-conn-security-multistream v0.2.0/go.mod h1:hZN4MjlNetKD3Rq5Jb/P5ohUnFLNzEAR4DLSzpn2QLU= github.com/libp2p/go-eventbus v0.1.0/go.mod h1:vROgu5cs5T7cv7POWlWxBaVLxfSegC5UGQf8A2eEmx4= github.com/libp2p/go-eventbus v0.2.1 h1:VanAdErQnpTioN2TowqNcOijf6YwhuODe4pPKSDpxGc= github.com/libp2p/go-eventbus v0.2.1/go.mod h1:jc2S4SoEVPP48H9Wpzm5aiGwUCBMfGhVhhBjyhhCJs8= github.com/libp2p/go-flow-metrics v0.0.1/go.mod h1:Iv1GH0sG8DtYN3SVJ2eG221wMiNpZxBdp967ls1g+k8= github.com/libp2p/go-flow-metrics v0.0.2/go.mod h1:HeoSNUrOJVK1jEpDqVEiUOIXqhbnS27omG0uWU5slZs= github.com/libp2p/go-flow-metrics v0.0.3 h1:8tAs/hSdNvUiLgtlSy3mxwxWP4I9y/jlkPFT7epKdeM= github.com/libp2p/go-flow-metrics v0.0.3/go.mod h1:HeoSNUrOJVK1jEpDqVEiUOIXqhbnS27omG0uWU5slZs= github.com/libp2p/go-libp2p v0.6.1/go.mod h1:CTFnWXogryAHjXAKEbOf1OWY+VeAP3lDMZkfEI5sT54= github.com/libp2p/go-libp2p v0.7.0/go.mod h1:hZJf8txWeCduQRDC/WSqBGMxaTHCOYHt2xSU1ivxn0k= github.com/libp2p/go-libp2p v0.7.4/go.mod h1:oXsBlTLF1q7pxr+9w6lqzS1ILpyHsaBPniVO7zIHGMw= github.com/libp2p/go-libp2p v0.8.1/go.mod h1:QRNH9pwdbEBpx5DTJYg+qxcVaDMAz3Ee/qDKwXujH5o= github.com/libp2p/go-libp2p v0.12.0/go.mod h1:FpHZrfC1q7nA8jitvdjKBDF31hguaC676g/nT9PgQM0= github.com/libp2p/go-libp2p v0.13.0 h1:tDdrXARSghmusdm0nf1U/4M8aj8Rr0V2IzQOXmbzQ3s= github.com/libp2p/go-libp2p v0.13.0/go.mod h1:pM0beYdACRfHO1WcJlp65WXyG2A6NqYM+t2DTVAJxMo= github.com/libp2p/go-libp2p-asn-util v0.0.0-20200825225859-85005c6cf052 h1:BM7aaOF7RpmNn9+9g6uTjGJ0cTzWr5j9i9IKeun2M8U= github.com/libp2p/go-libp2p-asn-util v0.0.0-20200825225859-85005c6cf052/go.mod h1:nRMRTab+kZuk0LnKZpxhOVH/ndsdr2Nr//Zltc/vwgo= github.com/libp2p/go-libp2p-autonat v0.1.1/go.mod h1:OXqkeGOY2xJVWKAGV2inNF5aKN/djNA3fdpCWloIudE= github.com/libp2p/go-libp2p-autonat v0.2.0/go.mod h1:DX+9teU4pEEoZUqR1PiMlqliONQdNbfzE1C718tcViI= github.com/libp2p/go-libp2p-autonat v0.2.1/go.mod h1:MWtAhV5Ko1l6QBsHQNSuM6b1sRkXrpk0/LqCr+vCVxI= github.com/libp2p/go-libp2p-autonat v0.2.2/go.mod h1:HsM62HkqZmHR2k1xgX34WuWDzk/nBwNHoeyyT4IWV6A= github.com/libp2p/go-libp2p-autonat v0.4.0 h1:3y8XQbpr+ssX8QfZUHekjHCYK64sj6/4hnf/awD4+Ug= github.com/libp2p/go-libp2p-autonat v0.4.0/go.mod h1:YxaJlpr81FhdOv3W3BTconZPfhaYivRdf53g+S2wobk= github.com/libp2p/go-libp2p-blankhost v0.1.1/go.mod h1:pf2fvdLJPsC1FsVrNP3DUUvMzUts2dsLLBEpo1vW1ro= github.com/libp2p/go-libp2p-blankhost v0.1.4/go.mod h1:oJF0saYsAXQCSfDq254GMNmLNz6ZTHTOvtF4ZydUvwU= github.com/libp2p/go-libp2p-blankhost v0.2.0 h1:3EsGAi0CBGcZ33GwRuXEYJLLPoVWyXJ1bcJzAJjINkk= github.com/libp2p/go-libp2p-blankhost v0.2.0/go.mod h1:eduNKXGTioTuQAUcZ5epXi9vMl+t4d8ugUBRQ4SqaNQ= github.com/libp2p/go-libp2p-circuit v0.1.4/go.mod h1:CY67BrEjKNDhdTk8UgBX1Y/H5c3xkAcs3gnksxY7osU= github.com/libp2p/go-libp2p-circuit v0.2.1/go.mod h1:BXPwYDN5A8z4OEY9sOfr2DUQMLQvKt/6oku45YUmjIo= github.com/libp2p/go-libp2p-circuit v0.4.0 h1:eqQ3sEYkGTtybWgr6JLqJY6QLtPWRErvFjFDfAOO1wc= github.com/libp2p/go-libp2p-circuit v0.4.0/go.mod h1:t/ktoFIUzM6uLQ+o1G6NuBl2ANhBKN9Bc8jRIk31MoA= github.com/libp2p/go-libp2p-core v0.0.1/go.mod h1:g/VxnTZ/1ygHxH3dKok7Vno1VfpvGcGip57wjTU4fco= github.com/libp2p/go-libp2p-core v0.0.4/go.mod h1:jyuCQP356gzfCFtRKyvAbNkyeuxb7OlyhWZ3nls5d2I= github.com/libp2p/go-libp2p-core v0.2.0/go.mod h1:X0eyB0Gy93v0DZtSYbEM7RnMChm9Uv3j7yRXjO77xSI= github.com/libp2p/go-libp2p-core v0.2.2/go.mod h1:8fcwTbsG2B+lTgRJ1ICZtiM5GWCWZVoVrLaDRvIRng0= github.com/libp2p/go-libp2p-core v0.2.4/go.mod h1:STh4fdfa5vDYr0/SzYYeqnt+E6KfEV5VxfIrm0bcI0g= github.com/libp2p/go-libp2p-core v0.2.5/go.mod h1:6+5zJmKhsf7yHn1RbmYDu08qDUpIUxGdqHuEZckmZOA= github.com/libp2p/go-libp2p-core v0.3.0/go.mod h1:ACp3DmS3/N64c2jDzcV429ukDpicbL6+TrrxANBjPGw= github.com/libp2p/go-libp2p-core v0.3.1/go.mod h1:thvWy0hvaSBhnVBaW37BvzgVV68OUhgJJLAa6almrII= github.com/libp2p/go-libp2p-core v0.4.0/go.mod h1:49XGI+kc38oGVwqSBhDEwytaAxgZasHhFfQKibzTls0= github.com/libp2p/go-libp2p-core v0.5.0/go.mod h1:49XGI+kc38oGVwqSBhDEwytaAxgZasHhFfQKibzTls0= github.com/libp2p/go-libp2p-core v0.5.1/go.mod h1:uN7L2D4EvPCvzSH5SrhR72UWbnSGpt5/a35Sm4upn4Y= github.com/libp2p/go-libp2p-core v0.5.3/go.mod h1:uN7L2D4EvPCvzSH5SrhR72UWbnSGpt5/a35Sm4upn4Y= github.com/libp2p/go-libp2p-core v0.5.4/go.mod h1:uN7L2D4EvPCvzSH5SrhR72UWbnSGpt5/a35Sm4upn4Y= github.com/libp2p/go-libp2p-core v0.5.5/go.mod h1:vj3awlOr9+GMZJFH9s4mpt9RHHgGqeHCopzbYKZdRjM= github.com/libp2p/go-libp2p-core v0.5.6/go.mod h1:txwbVEhHEXikXn9gfC7/UDDw7rkxuX0bJvM49Ykaswo= github.com/libp2p/go-libp2p-core v0.5.7/go.mod h1:txwbVEhHEXikXn9gfC7/UDDw7rkxuX0bJvM49Ykaswo= github.com/libp2p/go-libp2p-core v0.6.0/go.mod h1:txwbVEhHEXikXn9gfC7/UDDw7rkxuX0bJvM49Ykaswo= github.com/libp2p/go-libp2p-core v0.6.1/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= github.com/libp2p/go-libp2p-core v0.7.0/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= github.com/libp2p/go-libp2p-core v0.8.0 h1:5K3mT+64qDTKbV3yTdbMCzJ7O6wbNsavAEb8iqBvBcI= github.com/libp2p/go-libp2p-core v0.8.0/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= github.com/libp2p/go-libp2p-crypto v0.1.0 h1:k9MFy+o2zGDNGsaoZl0MA3iZ75qXxr9OOoAZF+sD5OQ= github.com/libp2p/go-libp2p-crypto v0.1.0/go.mod h1:sPUokVISZiy+nNuTTH/TY+leRSxnFj/2GLjtOTW90hI= github.com/libp2p/go-libp2p-discovery v0.2.0/go.mod h1:s4VGaxYMbw4+4+tsoQTqh7wfxg97AEdo4GYBt6BadWg= github.com/libp2p/go-libp2p-discovery v0.3.0/go.mod h1:o03drFnz9BVAZdzC/QUQ+NeQOu38Fu7LJGEOK2gQltw= github.com/libp2p/go-libp2p-discovery v0.5.0 h1:Qfl+e5+lfDgwdrXdu4YNCWyEo3fWuP+WgN9mN0iWviQ= github.com/libp2p/go-libp2p-discovery v0.5.0/go.mod h1:+srtPIU9gDaBNu//UHvcdliKBIcr4SfDcm0/PfPJLug= github.com/libp2p/go-libp2p-kad-dht v0.11.1 h1:FsriVQhOUZpCotWIjyFSjEDNJmUzuMma/RyyTDZanwc= github.com/libp2p/go-libp2p-kad-dht v0.11.1/go.mod h1:5ojtR2acDPqh/jXf5orWy8YGb8bHQDS+qeDcoscL/PI= github.com/libp2p/go-libp2p-kbucket v0.4.7 h1:spZAcgxifvFZHBD8tErvppbnNiKA5uokDu3CV7axu70= github.com/libp2p/go-libp2p-kbucket v0.4.7/go.mod h1:XyVo99AfQH0foSf176k4jY1xUJ2+jUJIZCSDm7r2YKk= github.com/libp2p/go-libp2p-loggables v0.1.0 h1:h3w8QFfCt2UJl/0/NW4K829HX/0S4KD31PQ7m8UXXO8= github.com/libp2p/go-libp2p-loggables v0.1.0/go.mod h1:EyumB2Y6PrYjr55Q3/tiJ/o3xoDasoRYM7nOzEpoa90= github.com/libp2p/go-libp2p-mplex v0.2.0/go.mod h1:Ejl9IyjvXJ0T9iqUTE1jpYATQ9NM3g+OtR+EMMODbKo= github.com/libp2p/go-libp2p-mplex v0.2.1/go.mod h1:SC99Rxs8Vuzrf/6WhmH41kNn13TiYdAWNYHrwImKLnE= github.com/libp2p/go-libp2p-mplex v0.2.2/go.mod h1:74S9eum0tVQdAfFiKxAyKzNdSuLqw5oadDq7+L/FELo= github.com/libp2p/go-libp2p-mplex v0.2.3/go.mod h1:CK3p2+9qH9x+7ER/gWWDYJ3QW5ZxWDkm+dVvjfuG3ek= github.com/libp2p/go-libp2p-mplex v0.3.0/go.mod h1:l9QWxRbbb5/hQMECEb908GbS9Sm2UAR2KFZKUJEynEs= github.com/libp2p/go-libp2p-mplex v0.4.0/go.mod h1:yCyWJE2sc6TBTnFpjvLuEJgTSw/u+MamvzILKdX7asw= github.com/libp2p/go-libp2p-mplex v0.4.1 h1:/pyhkP1nLwjG3OM+VuaNJkQT/Pqq73WzB3aDN3Fx1sc= github.com/libp2p/go-libp2p-mplex v0.4.1/go.mod h1:cmy+3GfqfM1PceHTLL7zQzAAYaryDu6iPSC+CIb094g= github.com/libp2p/go-libp2p-nat v0.0.5/go.mod h1:1qubaE5bTZMJE+E/uu2URroMbzdubFz1ChgiN79yKPE= github.com/libp2p/go-libp2p-nat v0.0.6 h1:wMWis3kYynCbHoyKLPBEMu4YRLltbm8Mk08HGSfvTkU= github.com/libp2p/go-libp2p-nat v0.0.6/go.mod h1:iV59LVhB3IkFvS6S6sauVTSOrNEANnINbI/fkaLimiw= github.com/libp2p/go-libp2p-netutil v0.1.0 h1:zscYDNVEcGxyUpMd0JReUZTrpMfia8PmLKcKF72EAMQ= github.com/libp2p/go-libp2p-netutil v0.1.0/go.mod h1:3Qv/aDqtMLTUyQeundkKsA+YCThNdbQD54k3TqjpbFU= github.com/libp2p/go-libp2p-noise v0.1.1 h1:vqYQWvnIcHpIoWJKC7Al4D6Hgj0H012TuXRhPwSMGpQ= github.com/libp2p/go-libp2p-noise v0.1.1/go.mod h1:QDFLdKX7nluB7DEnlVPbz7xlLHdwHFA9HiohJRr3vwM= github.com/libp2p/go-libp2p-peer v0.2.0 h1:EQ8kMjaCUwt/Y5uLgjT8iY2qg0mGUT0N1zUjer50DsY= github.com/libp2p/go-libp2p-peer v0.2.0/go.mod h1:RCffaCvUyW2CJmG2gAWVqwePwW7JMgxjsHm7+J5kjWY= github.com/libp2p/go-libp2p-peerstore v0.1.0/go.mod h1:2CeHkQsr8svp4fZ+Oi9ykN1HBb6u0MOvdJ7YIsmcwtY= github.com/libp2p/go-libp2p-peerstore v0.1.3/go.mod h1:BJ9sHlm59/80oSkpWgr1MyY1ciXAXV397W6h1GH/uKI= github.com/libp2p/go-libp2p-peerstore v0.1.4/go.mod h1:+4BDbDiiKf4PzpANZDAT+knVdLxvqh7hXOujessqdzs= github.com/libp2p/go-libp2p-peerstore v0.2.0/go.mod h1:N2l3eVIeAitSg3Pi2ipSrJYnqhVnMNQZo9nkSCuAbnQ= github.com/libp2p/go-libp2p-peerstore v0.2.1/go.mod h1:NQxhNjWxf1d4w6PihR8btWIRjwRLBr4TYKfNgrUkOPA= github.com/libp2p/go-libp2p-peerstore v0.2.2/go.mod h1:NQxhNjWxf1d4w6PihR8btWIRjwRLBr4TYKfNgrUkOPA= github.com/libp2p/go-libp2p-peerstore v0.2.6 h1:2ACefBX23iMdJU9Ke+dcXt3w86MIryes9v7In4+Qq3U= github.com/libp2p/go-libp2p-peerstore v0.2.6/go.mod h1:ss/TWTgHZTMpsU/oKVVPQCGuDHItOpf2W8RxAi50P2s= github.com/libp2p/go-libp2p-pnet v0.2.0 h1:J6htxttBipJujEjz1y0a5+eYoiPcFHhSYHH6na5f0/k= github.com/libp2p/go-libp2p-pnet v0.2.0/go.mod h1:Qqvq6JH/oMZGwqs3N1Fqhv8NVhrdYcO0BW4wssv21LA= github.com/libp2p/go-libp2p-quic-transport v0.10.0 h1:koDCbWD9CCHwcHZL3/WEvP2A+e/o5/W5L3QS/2SPMA0= github.com/libp2p/go-libp2p-quic-transport v0.10.0/go.mod h1:RfJbZ8IqXIhxBRm5hqUEJqjiiY8xmEuq3HUDS993MkA= github.com/libp2p/go-libp2p-record v0.1.2/go.mod h1:pal0eNcT5nqZaTV7UGhqeGqxFgGdsU/9W//C8dqjQDk= github.com/libp2p/go-libp2p-record v0.1.3 h1:R27hoScIhQf/A8XJZ8lYpnqh9LatJ5YbHs28kCIfql0= github.com/libp2p/go-libp2p-record v0.1.3/go.mod h1:yNUff/adKIfPnYQXgp6FQmNu3gLJ6EMg7+/vv2+9pY4= github.com/libp2p/go-libp2p-routing v0.1.0 h1:hFnj3WR3E2tOcKaGpyzfP4gvFZ3t8JkQmbapN0Ct+oU= github.com/libp2p/go-libp2p-routing v0.1.0/go.mod h1:zfLhI1RI8RLEzmEaaPwzonRvXeeSHddONWkcTcB54nE= github.com/libp2p/go-libp2p-routing-helpers v0.2.3/go.mod h1:795bh+9YeoFl99rMASoiVgHdi5bjack0N1+AFAdbvBw= github.com/libp2p/go-libp2p-secio v0.1.0/go.mod h1:tMJo2w7h3+wN4pgU2LSYeiKPrfqBgkOsdiKK77hE7c8= github.com/libp2p/go-libp2p-secio v0.2.0/go.mod h1:2JdZepB8J5V9mBp79BmwsaPQhRPNN2NrnB2lKQcdy6g= github.com/libp2p/go-libp2p-secio v0.2.1/go.mod h1:cWtZpILJqkqrSkiYcDBh5lA3wbT2Q+hz3rJQq3iftD8= github.com/libp2p/go-libp2p-secio v0.2.2/go.mod h1:wP3bS+m5AUnFA+OFO7Er03uO1mncHG0uVwGrwvjYlNY= github.com/libp2p/go-libp2p-swarm v0.1.0/go.mod h1:wQVsCdjsuZoc730CgOvh5ox6K8evllckjebkdiY5ta4= github.com/libp2p/go-libp2p-swarm v0.2.2/go.mod h1:fvmtQ0T1nErXym1/aa1uJEyN7JzaTNyBcHImCxRpPKU= github.com/libp2p/go-libp2p-swarm v0.2.3/go.mod h1:P2VO/EpxRyDxtChXz/VPVXyTnszHvokHKRhfkEgFKNM= github.com/libp2p/go-libp2p-swarm v0.2.8/go.mod h1:JQKMGSth4SMqonruY0a8yjlPVIkb0mdNSwckW7OYziM= github.com/libp2p/go-libp2p-swarm v0.3.0/go.mod h1:hdv95GWCTmzkgeJpP+GK/9D9puJegb7H57B5hWQR5Kk= github.com/libp2p/go-libp2p-swarm v0.3.1/go.mod h1:hdv95GWCTmzkgeJpP+GK/9D9puJegb7H57B5hWQR5Kk= github.com/libp2p/go-libp2p-swarm v0.4.0 h1:hahq/ijRoeH6dgROOM8x7SeaKK5VgjjIr96vdrT+NUA= github.com/libp2p/go-libp2p-swarm v0.4.0/go.mod h1:XVFcO52VoLoo0eitSxNQWYq4D6sydGOweTOAjJNraCw= github.com/libp2p/go-libp2p-testing v0.0.2/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= github.com/libp2p/go-libp2p-testing v0.0.3/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= github.com/libp2p/go-libp2p-testing v0.0.4/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= github.com/libp2p/go-libp2p-testing v0.1.0/go.mod h1:xaZWMJrPUM5GlDBxCeGUi7kI4eqnjVyavGroI2nxEM0= github.com/libp2p/go-libp2p-testing v0.1.1/go.mod h1:xaZWMJrPUM5GlDBxCeGUi7kI4eqnjVyavGroI2nxEM0= github.com/libp2p/go-libp2p-testing v0.1.2-0.20200422005655-8775583591d8/go.mod h1:Qy8sAncLKpwXtS2dSnDOP8ktexIAHKu+J+pnZOFZLTc= github.com/libp2p/go-libp2p-testing v0.3.0/go.mod h1:efZkql4UZ7OVsEfaxNHZPzIehtsBXMrXnCfJIgDti5g= github.com/libp2p/go-libp2p-testing v0.4.0 h1:PrwHRi0IGqOwVQWR3xzgigSlhlLfxgfXgkHxr77EghQ= github.com/libp2p/go-libp2p-testing v0.4.0/go.mod h1:Q+PFXYoiYFN5CAEG2w3gLPEzotlKsNSbKQ/lImlOWF0= github.com/libp2p/go-libp2p-tls v0.1.3 h1:twKMhMu44jQO+HgQK9X8NHO5HkeJu2QbhLzLJpa8oNM= github.com/libp2p/go-libp2p-tls v0.1.3/go.mod h1:wZfuewxOndz5RTnCAxFliGjvYSDA40sKitV4c50uI1M= github.com/libp2p/go-libp2p-transport-upgrader v0.1.1/go.mod h1:IEtA6or8JUbsV07qPW4r01GnTenLW4oi3lOPbUMGJJA= github.com/libp2p/go-libp2p-transport-upgrader v0.2.0/go.mod h1:mQcrHj4asu6ArfSoMuyojOdjx73Q47cYD7s5+gZOlns= github.com/libp2p/go-libp2p-transport-upgrader v0.3.0/go.mod h1:i+SKzbRnvXdVbU3D1dwydnTmKRPXiAR/fyvi1dXuL4o= github.com/libp2p/go-libp2p-transport-upgrader v0.4.0 h1:xwj4h3hJdBrxqMOyMUjwscjoVst0AASTsKtZiTChoHI= github.com/libp2p/go-libp2p-transport-upgrader v0.4.0/go.mod h1:J4ko0ObtZSmgn5BX5AmegP+dK3CSnU2lMCKsSq/EY0s= github.com/libp2p/go-libp2p-yamux v0.2.0/go.mod h1:Db2gU+XfLpm6E4rG5uGCFX6uXA8MEXOxFcRoXUODaK8= github.com/libp2p/go-libp2p-yamux v0.2.2/go.mod h1:lIohaR0pT6mOt0AZ0L2dFze9hds9Req3OfS+B+dv4qw= github.com/libp2p/go-libp2p-yamux v0.2.5/go.mod h1:Zpgj6arbyQrmZ3wxSZxfBmbdnWtbZ48OpsfmQVTErwA= github.com/libp2p/go-libp2p-yamux v0.2.7/go.mod h1:X28ENrBMU/nm4I3Nx4sZ4dgjZ6VhLEn0XhIoZ5viCwU= github.com/libp2p/go-libp2p-yamux v0.2.8/go.mod h1:/t6tDqeuZf0INZMTgd0WxIRbtK2EzI2h7HbFm9eAKI4= github.com/libp2p/go-libp2p-yamux v0.4.0/go.mod h1:+DWDjtFMzoAwYLVkNZftoucn7PelNoy5nm3tZ3/Zw30= github.com/libp2p/go-libp2p-yamux v0.5.0/go.mod h1:AyR8k5EzyM2QN9Bbdg6X1SkVVuqLwTGf0L4DFq9g6po= github.com/libp2p/go-libp2p-yamux v0.5.1 h1:sX4WQPHMhRxJE5UZTfjEuBvlQWXB5Bo3A2JK9ZJ9EM0= github.com/libp2p/go-libp2p-yamux v0.5.1/go.mod h1:dowuvDu8CRWmr0iqySMiSxK+W0iL5cMVO9S94Y6gkv4= github.com/libp2p/go-maddr-filter v0.0.4/go.mod h1:6eT12kSQMA9x2pvFQa+xesMKUBlj9VImZbj3B9FBH/Q= github.com/libp2p/go-maddr-filter v0.0.5/go.mod h1:Jk+36PMfIqCJhAnaASRH83bdAvfDRp/w6ENFaC9bG+M= github.com/libp2p/go-maddr-filter v0.1.0/go.mod h1:VzZhTXkMucEGGEOSKddrwGiOv0tUhgnKqNEmIAz/bPU= github.com/libp2p/go-mplex v0.0.3/go.mod h1:pK5yMLmOoBR1pNCqDlA2GQrdAVTMkqFalaTWe7l4Yd0= github.com/libp2p/go-mplex v0.1.0/go.mod h1:SXgmdki2kwCUlCCbfGLEgHjC4pFqhTp0ZoV6aiKgxDU= github.com/libp2p/go-mplex v0.1.1/go.mod h1:Xgz2RDCi3co0LeZfgjm4OgUF15+sVR8SRcu3SFXI1lk= github.com/libp2p/go-mplex v0.1.2/go.mod h1:Xgz2RDCi3co0LeZfgjm4OgUF15+sVR8SRcu3SFXI1lk= github.com/libp2p/go-mplex v0.2.0/go.mod h1:0Oy/A9PQlwBytDRp4wSkFnzHYDKcpLot35JQ6msjvYQ= github.com/libp2p/go-mplex v0.3.0 h1:U1T+vmCYJaEoDJPV1aq31N56hS+lJgb397GsylNSgrU= github.com/libp2p/go-mplex v0.3.0/go.mod h1:0Oy/A9PQlwBytDRp4wSkFnzHYDKcpLot35JQ6msjvYQ= github.com/libp2p/go-msgio v0.0.2/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ= github.com/libp2p/go-msgio v0.0.4/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ= github.com/libp2p/go-msgio v0.0.6 h1:lQ7Uc0kS1wb1EfRxO2Eir/RJoHkHn7t6o+EiwsYIKJA= github.com/libp2p/go-msgio v0.0.6/go.mod h1:4ecVB6d9f4BDSL5fqvPiC4A3KivjWn+Venn/1ALLMWA= github.com/libp2p/go-nat v0.0.4/go.mod h1:Nmw50VAvKuk38jUBcmNh6p9lUJLoODbJRvYAa/+KSDo= github.com/libp2p/go-nat v0.0.5 h1:qxnwkco8RLKqVh1NmjQ+tJ8p8khNLFxuElYG/TwqW4Q= github.com/libp2p/go-nat v0.0.5/go.mod h1:B7NxsVNPZmRLvMOwiEO1scOSyjA56zxYAGv1yQgRkEU= github.com/libp2p/go-netroute v0.1.2/go.mod h1:jZLDV+1PE8y5XxBySEBgbuVAXbhtuHSdmLPL2n9MKbk= github.com/libp2p/go-netroute v0.1.3 h1:1ngWRx61us/EpaKkdqkMjKk/ufr/JlIFYQAxV2XX8Ig= github.com/libp2p/go-netroute v0.1.3/go.mod h1:jZLDV+1PE8y5XxBySEBgbuVAXbhtuHSdmLPL2n9MKbk= github.com/libp2p/go-openssl v0.0.2/go.mod h1:v8Zw2ijCSWBQi8Pq5GAixw6DbFfa9u6VIYDXnvOXkc0= github.com/libp2p/go-openssl v0.0.3/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= github.com/libp2p/go-openssl v0.0.4/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= github.com/libp2p/go-openssl v0.0.5/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= github.com/libp2p/go-openssl v0.0.7 h1:eCAzdLejcNVBzP/iZM9vqHnQm+XyCEbSSIheIPRGNsw= github.com/libp2p/go-openssl v0.0.7/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= github.com/libp2p/go-reuseport v0.0.1/go.mod h1:jn6RmB1ufnQwl0Q1f+YxAj8isJgDCQzaaxIFYDhcYEA= github.com/libp2p/go-reuseport v0.0.2 h1:XSG94b1FJfGA01BUrT82imejHQyTxO4jEWqheyCXYvU= github.com/libp2p/go-reuseport v0.0.2/go.mod h1:SPD+5RwGC7rcnzngoYC86GjPzjSywuQyMVAheVBD9nQ= github.com/libp2p/go-reuseport-transport v0.0.2/go.mod h1:YkbSDrvjUVDL6b8XqriyA20obEtsW9BLkuOUyQAOCbs= github.com/libp2p/go-reuseport-transport v0.0.3/go.mod h1:Spv+MPft1exxARzP2Sruj2Wb5JSyHNncjf1Oi2dEbzM= github.com/libp2p/go-reuseport-transport v0.0.4 h1:OZGz0RB620QDGpv300n1zaOcKGGAoGVf8h9txtt/1uM= github.com/libp2p/go-reuseport-transport v0.0.4/go.mod h1:trPa7r/7TJK/d+0hdBLOCGvpQQVOU74OXbNCIMkufGw= github.com/libp2p/go-sockaddr v0.0.2 h1:tCuXfpA9rq7llM/v834RKc/Xvovy/AqM9kHvTV/jY/Q= github.com/libp2p/go-sockaddr v0.0.2/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k= github.com/libp2p/go-stream-muxer v0.0.1/go.mod h1:bAo8x7YkSpadMTbtTaxGVHWUQsR/l5MEaHbKaliuT14= github.com/libp2p/go-stream-muxer-multistream v0.2.0/go.mod h1:j9eyPol/LLRqT+GPLSxvimPhNph4sfYfMoDPd7HkzIc= github.com/libp2p/go-stream-muxer-multistream v0.3.0 h1:TqnSHPJEIqDEO7h1wZZ0p3DXdvDSiLHQidKKUGZtiOY= github.com/libp2p/go-stream-muxer-multistream v0.3.0/go.mod h1:yDh8abSIzmZtqtOt64gFJUXEryejzNb0lisTt+fAMJA= github.com/libp2p/go-tcp-transport v0.1.0/go.mod h1:oJ8I5VXryj493DEJ7OsBieu8fcg2nHGctwtInJVpipc= github.com/libp2p/go-tcp-transport v0.1.1/go.mod h1:3HzGvLbx6etZjnFlERyakbaYPdfjg2pWP97dFZworkY= github.com/libp2p/go-tcp-transport v0.2.0/go.mod h1:vX2U0CnWimU4h0SGSEsg++AzvBcroCGYw28kh94oLe0= github.com/libp2p/go-tcp-transport v0.2.1 h1:ExZiVQV+h+qL16fzCWtd1HSzPsqWottJ8KXwWaVi8Ns= github.com/libp2p/go-tcp-transport v0.2.1/go.mod h1:zskiJ70MEfWz2MKxvFB/Pv+tPIB1PpPUrHIWQ8aFw7M= github.com/libp2p/go-ws-transport v0.2.0/go.mod h1:9BHJz/4Q5A9ludYWKoGCFC5gUElzlHoKzu0yY9p/klM= github.com/libp2p/go-ws-transport v0.3.0/go.mod h1:bpgTJmRZAvVHrgHybCVyqoBmyLQ1fiZuEaBYusP5zsk= github.com/libp2p/go-ws-transport v0.3.1/go.mod h1:bpgTJmRZAvVHrgHybCVyqoBmyLQ1fiZuEaBYusP5zsk= github.com/libp2p/go-ws-transport v0.4.0 h1:9tvtQ9xbws6cA5LvqdE6Ne3vcmGB4f1z9SByggk4s0k= github.com/libp2p/go-ws-transport v0.4.0/go.mod h1:EcIEKqf/7GDjth6ksuS/6p7R49V4CBY6/E7R/iyhYUA= github.com/libp2p/go-yamux v1.2.2/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= github.com/libp2p/go-yamux v1.3.0/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= github.com/libp2p/go-yamux v1.3.3/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= github.com/libp2p/go-yamux v1.3.5/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= github.com/libp2p/go-yamux v1.3.7/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE= github.com/libp2p/go-yamux v1.4.0/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE= github.com/libp2p/go-yamux v1.4.1 h1:P1Fe9vF4th5JOxxgQvfbOHkrGqIZniTLf+ddhZp8YTI= github.com/libp2p/go-yamux v1.4.1/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE= github.com/libp2p/go-yamux/v2 v2.0.0 h1:vSGhAy5u6iHBq11ZDcyHH4Blcf9xlBhT4WQDoOE90LU= github.com/libp2p/go-yamux/v2 v2.0.0/go.mod h1:NVWira5+sVUIU6tu1JWvaRn1dRnG+cawOJiflsAM+7U= github.com/lucas-clemente/quic-go v0.19.3 h1:eCDQqvGBB+kCTkA0XrAFtNe81FMa0/fn4QSoeAbmiF4= github.com/lucas-clemente/quic-go v0.19.3/go.mod h1:ADXpNbTQjq1hIzCpB+y/k5iz4n4z4IwqoLb94Kh5Hu8= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc= github.com/marten-seemann/qtls v0.10.0 h1:ECsuYUKalRL240rRD4Ri33ISb7kAQ3qGDlrrl55b2pc= github.com/marten-seemann/qtls v0.10.0/go.mod h1:UvMd1oaYDACI99/oZUYLzMCkBXQVT0aGm99sJhbT8hs= github.com/marten-seemann/qtls-go1-15 v0.1.1 h1:LIH6K34bPVttyXnUWixk0bzH6/N07VxbSabxn5A5gZQ= github.com/marten-seemann/qtls-go1-15 v0.1.1/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/miekg/dns v1.1.12/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.28/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/miekg/dns v1.1.31/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 h1:lYpkrQH5ajf0OXOcUbGjvZxxijuBwbbmlSxLiuofa+g= github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= github.com/minio/sha256-simd v0.0.0-20190328051042-05b4dd3047e5/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= github.com/minio/sha256-simd v0.1.0/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU= github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= github.com/mr-tron/base58 v1.1.1/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/multiformats/go-base32 v0.0.3 h1:tw5+NhuwaOjJCC5Pp82QuXbrmLzWg7uxlMFp8Nq/kkI= github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA= github.com/multiformats/go-base36 v0.1.0 h1:JR6TyF7JjGd3m6FbLU2cOxhC0Li8z8dLNGQ89tUg4F4= github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM= github.com/multiformats/go-multiaddr v0.0.1/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= github.com/multiformats/go-multiaddr v0.0.2/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= github.com/multiformats/go-multiaddr v0.0.4/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= github.com/multiformats/go-multiaddr v0.1.0/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo= github.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y95ncPj0vFwVF6k6wJ4= github.com/multiformats/go-multiaddr v0.2.1/go.mod h1:s/Apk6IyxfvMjDafnhJgJ3/46z7tZ04iMk5wP4QMGGE= github.com/multiformats/go-multiaddr v0.2.2/go.mod h1:NtfXiOtHvghW9KojvtySjH5y0u0xW5UouOmQQrn6a3Y= github.com/multiformats/go-multiaddr v0.3.0/go.mod h1:dF9kph9wfJ+3VLAaeBqo9Of8x4fJxp6ggJGteB8HQTI= github.com/multiformats/go-multiaddr v0.3.1 h1:1bxa+W7j9wZKTZREySx1vPMs2TqrYWjVZ7zE6/XLG1I= github.com/multiformats/go-multiaddr v0.3.1/go.mod h1:uPbspcUPd5AfaP6ql3ujFY+QWzmBD8uLLL4bXW0XfGc= github.com/multiformats/go-multiaddr-dns v0.0.1/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= github.com/multiformats/go-multiaddr-dns v0.0.2/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= github.com/multiformats/go-multiaddr-dns v0.2.0 h1:YWJoIDwLePniH7OU5hBnDZV6SWuvJqJ0YtN6pLeH9zA= github.com/multiformats/go-multiaddr-dns v0.2.0/go.mod h1:TJ5pr5bBO7Y1B18djPuRsVkduhQH2YqYSbxWJzYGdK0= github.com/multiformats/go-multiaddr-fmt v0.0.1/go.mod h1:aBYjqL4T/7j4Qx+R73XSv/8JsgnRFlf0w2KGLCmXl3Q= github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E= github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo= github.com/multiformats/go-multiaddr-net v0.0.1/go.mod h1:nw6HSxNmCIQH27XPGBuX+d1tnvM7ihcFwHMSstNAVUU= github.com/multiformats/go-multiaddr-net v0.1.0/go.mod h1:5JNbcfBOP4dnhoZOv10JJVkJO0pCCEf8mTnipAo2UZQ= github.com/multiformats/go-multiaddr-net v0.1.1/go.mod h1:5JNbcfBOP4dnhoZOv10JJVkJO0pCCEf8mTnipAo2UZQ= github.com/multiformats/go-multiaddr-net v0.1.2/go.mod h1:QsWt3XK/3hwvNxZJp92iMQKME1qHfpYmyIjFVsSOY6Y= github.com/multiformats/go-multiaddr-net v0.1.3/go.mod h1:ilNnaM9HbmVFqsb/qcNysjCu4PVONlrBZpHIrw/qQuA= github.com/multiformats/go-multiaddr-net v0.1.4/go.mod h1:ilNnaM9HbmVFqsb/qcNysjCu4PVONlrBZpHIrw/qQuA= github.com/multiformats/go-multiaddr-net v0.1.5/go.mod h1:ilNnaM9HbmVFqsb/qcNysjCu4PVONlrBZpHIrw/qQuA= github.com/multiformats/go-multiaddr-net v0.2.0 h1:MSXRGN0mFymt6B1yo/6BPnIRpLPEnKgQNvVfCX5VDJk= github.com/multiformats/go-multiaddr-net v0.2.0/go.mod h1:gGdH3UXny6U3cKKYCvpXI5rnK7YaOIEOPVDI9tsJbEA= github.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs= github.com/multiformats/go-multibase v0.0.3 h1:l/B6bJDQjvQ5G52jw4QGSYeOTZoAwIO77RblWplfIqk= github.com/multiformats/go-multibase v0.0.3/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc= github.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U= github.com/multiformats/go-multihash v0.0.5/go.mod h1:lt/HCbqlQwlPBz7lv0sQCdtfcMtlJvakRUn/0Ual8po= github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= github.com/multiformats/go-multihash v0.0.9/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= github.com/multiformats/go-multihash v0.0.10/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= github.com/multiformats/go-multihash v0.0.13/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= github.com/multiformats/go-multihash v0.0.14 h1:QoBceQYQQtNUuf6s7wHxnE2c8bhbMqhfGzNI032se/I= github.com/multiformats/go-multihash v0.0.14/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= github.com/multiformats/go-multistream v0.1.0/go.mod h1:fJTiDfXJVmItycydCnNx4+wSzZ5NwG2FEVAI30fiovg= github.com/multiformats/go-multistream v0.1.1/go.mod h1:KmHZ40hzVxiaiwlj3MEbYgK9JFk2/9UktWZAF54Du38= github.com/multiformats/go-multistream v0.2.0 h1:6AuNmQVKUkRnddw2YiDjt5Elit40SFxMJkVnhmETXtU= github.com/multiformats/go-multistream v0.2.0/go.mod h1:5GZPQZbkWOLOn3J2y4Y99vVW7vOfsAflxARk3x14o6k= github.com/multiformats/go-varint v0.0.1/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= github.com/multiformats/go-varint v0.0.2/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= github.com/multiformats/go-varint v0.0.6 h1:gk85QWKxh3TazbLxED/NlDVv8+q+ReFJk7Y2W/KhfNY= github.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys= github.com/peterbourgon/ff/v3 v3.0.0 h1:eQzEmNahuOjQXfuegsKQTSTDbf4dNvr/eNLrmJhiH7M= github.com/peterbourgon/ff/v3 v3.0.0/go.mod h1:UILIFjRH5a/ar8TjXYLTkIvSvekZqPm5Eb/qbGk6CT0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0= github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw= github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI= github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU= github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag= github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg= github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw= github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y= github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q= github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ= github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I= github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0= github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ= github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk= github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/smola/gocompat v0.2.0/go.mod h1:1B0MlxbmoZNo3h8guHp8HztB3BSYR5itql9qtVc0ypY= github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= github.com/spacemonkeygo/openssl v0.0.0-20181017203307-c2dcc5cca94a/go.mod h1:7AyxJNCJ7SBZ1MfVQCWD6Uqo2oubI2Eq2y2eqf+A5r0= github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 h1:RC6RW7j+1+HkWaX/Yh71Ee5ZHaHYt7ZP4sQgUrm6cDU= github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/src-d/envconfig v1.0.0/go.mod h1:Q9YQZ7BKITldTBnoxsE5gOeB5y66RyPXeue/R4aaNBc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 h1:EKhdznlJHPMoKr0XTrX+IlJs1LH3lyx2nfr1dOlZ79k= github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1/go.mod h1:8UvriyWtv5Q5EOgjHaSseUEdkQfvwFv1I/In/O2M9gc= github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc/go.mod h1:bopw91TMyo8J3tvftk8xmU2kPmlrt4nScJQZU2hE5EM= github.com/whyrusleeping/go-logging v0.0.1/go.mod h1:lDPYj54zutzG1XYfHAhcc7oNXEburHQBn+Iqd4yS4vE= github.com/whyrusleeping/mafmt v1.2.8/go.mod h1:faQJFPbLSxzD9xpA02ttW/tS9vZykNvXwGvqIpk20FA= github.com/whyrusleeping/mdns v0.0.0-20190826153040-b9b60ed33aa9/go.mod h1:j4l84WPFclQPj320J9gp0XwNKBb3U0zt5CBqjPp22G4= github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 h1:E9S12nwJwEOXe2d6gT6qxdvqMnNq+VnSsKPgm2ZZNds= github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7/go.mod h1:X2c0RVCI1eSUFI8eLcY3c0423ykwiUdxLJtkDvruhjI= github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4 h1:LYy1Hy3MJdrCdMwwzxA/dRok4ejH+RwNGbuoD9fCjto= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/goleak v1.0.0 h1:qsup4IcBdlmsnGfqyLl4Ntn3C2XCCuKAE7DwHpScyUo= go.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.14.1/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM= go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM= go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190225124518-7f87c0fbb88b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190227160552-c95aed5357e7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190526052359-791d8a0f4d09/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181130052023-1c3d964395ce/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 h1:VvQyQJN0tSuecqgcIxMWnnfG5kSmgy9KZR9sW3W5QeA= golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.28.1/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.31.1 h1:SfXqXS5hkufcdZ/mHtYCh53P2b+92WQq/DZcKLgsFRs= google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/src-d/go-cli.v0 v0.0.0-20181105080154-d492247bbc0d/go.mod h1:z+K8VcOYVYcSwSjGebuDL6176A1XskgbtNl64NSg+n8= gopkg.in/src-d/go-log.v1 v1.0.1/go.mod h1:GN34hKP0g305ysm2/hctJ0Y8nWP3zxXXJ8GFabTyABE= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= ================================================ FILE: tool/bench-cellular/server.go ================================================ package main import ( "bufio" "context" "fmt" "io" "log" "math/rand" "strconv" "github.com/libp2p/go-libp2p" dht "github.com/libp2p/go-libp2p-kad-dht" routing "github.com/libp2p/go-libp2p-routing" "github.com/libp2p/go-libp2p/core/event" "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/network" "github.com/libp2p/go-libp2p/core/peer" ma "github.com/multiformats/go-multiaddr" manet "github.com/multiformats/go-multiaddr/net" ) var tcpBertyRelays = []string{ "/ip4/51.159.21.214/tcp/4040/p2p/QmdT7AmhhnbuwvCpa5PH1ySK9HJVB82jr3fo1bxMxBPW6p", "/ip4/51.15.25.224/tcp/4040/p2p/12D3KooWHhDBv6DJJ4XDWjzEXq6sVNEs6VuxsV1WyBBEhPENHzcZ", } var quicBertyRelays = []string{ "/ip4/51.159.21.214/udp/4040/quic/p2p/QmdT7AmhhnbuwvCpa5PH1ySK9HJVB82jr3fo1bxMxBPW6p", "/ip4/51.15.25.224/udp/4040/quic/p2p/12D3KooWHhDBv6DJJ4XDWjzEXq6sVNEs6VuxsV1WyBBEhPENHzcZ", } var tcpIPFSRelays = []string{ "/ip4/147.75.80.110/tcp/4001/p2p/QmbFgm5zan8P6eWWmeyfncR5feYEMPbht5b1FW1C37aQ7y", "/ip4/147.75.195.153/tcp/4001/p2p/QmW9m57aiBDHAkKj9nmFSEn7ZqrcF1fZS4bipsTCHburei", "/ip4/147.75.70.221/tcp/4001/p2p/Qme8g49gm3q4Acp7xWBKg3nAa9fxZ1YmyDJdyGgoG6LsXh", } var quicIPFSRelays = []string{ "/ip4/147.75.80.110/udp/4001/quic/p2p/QmbFgm5zan8P6eWWmeyfncR5feYEMPbht5b1FW1C37aQ7y", "/ip4/147.75.195.153/udp/4001/quic/p2p/QmW9m57aiBDHAkKj9nmFSEn7ZqrcF1fZS4bipsTCHburei", "/ip4/147.75.70.221/udp/4001/quic/p2p/Qme8g49gm3q4Acp7xWBKg3nAa9fxZ1YmyDJdyGgoG6LsXh", } const ( staticBertyRelayMode = "static-berty" staticIPFSRelayMode = "static-ipfs" discoveryRelayMode = "discovery" disabledRelayMode = "none" ) type serverOpts struct { port int ip6 bool relay string } func createServerHost(ctx context.Context, gOpts *globalOpts, sOpts *serverOpts) (host.Host, error) { opts, err := globalOptsToLibp2pOpts(gOpts) // Get identity and transport if err != nil { return nil, err } if sOpts.relay == disabledRelayMode { // If no relay, add relevant listener if sOpts.ip6 { if gOpts.tcp { opts = append(opts, libp2p.ListenAddrStrings(fmt.Sprintf("/ip6/::/tcp/%d", sOpts.port))) } else { opts = append(opts, libp2p.ListenAddrStrings(fmt.Sprintf("/ip6/::/udp/%d/quic", sOpts.port))) } } else { if gOpts.tcp { opts = append(opts, libp2p.ListenAddrStrings(fmt.Sprintf("/ip4/0.0.0.0/tcp/%d", sOpts.port))) } else { opts = append(opts, libp2p.ListenAddrStrings(fmt.Sprintf("/ip4/0.0.0.0/udp/%d/quic", sOpts.port))) } } opts = append(opts, libp2p.NATPortMap()) // Open port on NAT for access through public IP opts = append(opts, libp2p.DisableRelay()) } else { opts = append(opts, libp2p.ListenAddrs()) // If using relay, set no listener opts = append(opts, libp2p.EnableAutoRelay()) opts = append(opts, libp2p.ForceReachabilityPrivate()) } // Relay discovery needs DHT routing to discover relays // NATPortMap (relay disabled) needs DHT routing to get host public IP if sOpts.relay == discoveryRelayMode || sOpts.relay == disabledRelayMode { opts = append( opts, libp2p.Routing(func(h host.Host) (routing.PeerRouting, error) { return dht.New(ctx, h, dht.Mode(dht.ModeClient), dht.BootstrapPeers(dht.GetDefaultBootstrapPeerAddrInfos()...)) }), ) } else { // Or setup Berty / IPFS static relays var ( maddrRelays []string staticRelays []peer.AddrInfo ) if sOpts.relay == staticBertyRelayMode { if gOpts.tcp { maddrRelays = tcpBertyRelays } else { maddrRelays = quicBertyRelays } } else { if gOpts.tcp { maddrRelays = tcpIPFSRelays } else { maddrRelays = quicIPFSRelays } } for _, addr := range maddrRelays { maddr, err := ma.NewMultiaddr(addr) if err != nil { log.Printf("error: can't parse Multiaddr: %v\n", err) continue } pi, err := peer.AddrInfoFromP2pAddr(maddr) if err != nil { log.Printf("error: can't parse AddrInfo: %v\n", err) continue } staticRelays = append(staticRelays, *pi) } opts = append(opts, libp2p.StaticRelays(staticRelays)) } return libp2p.New(ctx, opts...) // Create host } func printHint(h host.Host, gOpts *globalOpts, sOpts *serverOpts) { var serverAddr ma.Multiaddr if sOpts.relay == disabledRelayMode { log.Print("Waiting for public address...") } else { log.Print("Waiting for relay address...") } eventReceiver, err := h.EventBus().Subscribe(new(event.EvtLocalAddressesUpdated)) if err != nil { log.Fatalf("can't subscribe to local addresses updated events: %v", err) } defer eventReceiver.Close() for ev := range eventReceiver.Out() { serverAddr = nil update := ev.(event.EvtLocalAddressesUpdated) for _, addr := range update.Current { if addr.Action != event.Added { continue } if sOpts.relay != disabledRelayMode { if _, err := addr.Address.ValueForProtocol(ma.P_CIRCUIT); err != nil { continue } if gOpts.tcp { if _, err := addr.Address.ValueForProtocol(ma.P_TCP); err != nil { continue } serverAddr = addr.Address } else { if _, err := addr.Address.ValueForProtocol(ma.P_QUIC); err != nil { continue } serverAddr = addr.Address } } else if sOpts.ip6 { if _, err := addr.Address.ValueForProtocol(ma.P_IP6); err != nil { continue } if manet.IsPublicAddr(addr.Address) { serverAddr = addr.Address } } else { if _, err := addr.Address.ValueForProtocol(ma.P_IP4); err != nil { continue } if manet.IsPublicAddr(addr.Address) { serverAddr = addr.Address } } } if serverAddr != nil { hostAddr, err := ma.NewMultiaddr(fmt.Sprintf("/ipfs/%s", h.ID().String())) if err != nil { panic(err) } fullAddr := serverAddr.Encapsulate(hostAddr) hint := "Now run: './bench" if gOpts.insecure { hint += " -insecure" } if gOpts.tcp { hint += " -tcp" } hint += fmt.Sprintf(" client -dest %s [-request ...] [-size X] [-reco]'", fullAddr) log.Println(hint) } } } func runServer(ctx context.Context, gOpts *globalOpts, sOpts *serverOpts) error { h, err := createServerHost(ctx, gOpts, sOpts) if err != nil { return fmt.Errorf("server host creation failed: %v", err) } go printHint(h, gOpts, sOpts) h.SetStreamHandler(benchDownloadPID, func(s network.Stream) { defer s.Close() remotePeerID := s.Conn().RemotePeer().String() log.Printf("New download stream from: %s\n", remotePeerID) _, err := s.Write([]byte("\n")) if err != nil { log.Printf("Write error during stream opened ack to %s: %v\n", remotePeerID, err) } buf := bufio.NewReader(s) str, err := buf.ReadString('\n') if err != nil { log.Printf("Read error during download from %s: %v\n", remotePeerID, err) return } size, err := strconv.Atoi(string(str[:len(str)-1])) if err != nil { log.Printf("Invalid size received: %s: %v", str, err) return } data := make([]byte, size) rand.Read(data) _, err = s.Write(data) if err != nil { log.Printf("Write error during download to %s: %v\n", remotePeerID, err) } log.Printf("Sent %d bytes to %s", len(data), remotePeerID) }) h.SetStreamHandler(benchUploadPID, func(s network.Stream) { defer s.Close() remotePeerID := s.Conn().RemotePeer().String() log.Printf("New upload stream from: %s\n", remotePeerID) _, err := s.Write([]byte("\n")) if err != nil { log.Printf("Write error during stream opened ack to %s: %v\n", remotePeerID, err) } reader := bufio.NewReader(s) data, err := io.ReadAll(reader) if err != nil { log.Printf("Read error during upload from %s: %v\n", remotePeerID, err) return } log.Printf("Received %d bytes from %s", len(data), remotePeerID) _, err = s.Write([]byte("\n")) if err != nil { log.Printf("Write error during uploaded ack to %s: %v\n", remotePeerID, err) } }) select {} } ================================================ FILE: tool/docker-protoc/Dockerfile ================================================ FROM moul/protoc-gen-gotemplate:latest as pgg # build image FROM golang:1.22-alpine as builder # install deps RUN apk --no-cache add make git go rsync libc-dev openssh docker npm bash curl # ensure we use bash for all RUN commands SHELL ["/bin/bash", "-c"] RUN git clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.12.0 && \ echo -e '\n. $HOME/.asdf/asdf.sh' >> ~/.bashrc # install compilers WORKDIR $GOPATH/src/berty.tech/weshnet COPY go.mod go.sum .tool-versions ./ # ensure modifications to bashrc are properly sourced ENV BASH_ENV=~/.bashrc # @TODO(gfanton): use asdf version RUN go install -mod=readonly \ google.golang.org/protobuf/cmd/protoc-gen-go \ github.com/srikrsna/protoc-gen-gotag \ google.golang.org/grpc/cmd/protoc-gen-go-grpc \ github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway \ github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger \ github.com/pseudomuto/protoc-gen-doc/cmd/protoc-gen-doc \ golang.org/x/tools/cmd/goimports RUN asdf plugin add buf && asdf install buf && \ cp $(asdf which buf) /go/bin/buf # runtime FROM golang:1.22-alpine RUN apk --no-cache add git openssh make protobuf gcc libc-dev nodejs npm yarn sudo perl-utils tar sed grep \ && mkdir -p /.cache/go-build \ && chmod -R 777 /.cache \ && npm install -g eclint COPY --from=pgg /go/bin/* /go/bin/ COPY --from=builder /go/bin/* /go/bin/ COPY --from=pgg /protobuf /protobuf ENV GOPATH=/go \ PATH=/go/bin:/node/node_modules/.bin:${PATH} \ GOROOT=/usr/local/go ================================================ FILE: tool/docker-protoc/Makefile ================================================ IMAGE ?= bertytech/buf VERSION ?= 5 build: cd ../../ && docker build -f ./tool/docker-protoc/Dockerfile -t $(IMAGE):$(VERSION) -t $(IMAGE):latest . publish: build docker push $(IMAGE):$(VERSION) docker push $(IMAGE):latest ================================================ FILE: tyber.go ================================================ package weshnet const ( TyberEventTinderPeerFound = "Tinder peer found" TyberEventTinderPeerJoined = "Tinder peer joined" TyberEventTinderPeerLeft = "Tinder peer left" )